Skip to content

Commit 0b85afc

Browse files
committed
all: rework HTTP service plumbing
Instead of making separate endpoints for each service, multiplex them all onto a single address configured by the new --http flag. Convert --modproxy into a Boolean flag, and collapse the --revcache flags to a single flag specifying the target hosts. Reorganize the reverse proxy plumbing so that it does not require weirdly structured URLs anymore; this makes things work better with proxy environments. Update the help text throughout.
1 parent 89bee48 commit 0b85afc

File tree

7 files changed

+154
-148
lines changed

7 files changed

+154
-148
lines changed

cmd/go-cache-plugin/commands.go

Lines changed: 32 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,9 @@ func runDirect(env *command.Env) error {
120120

121121
var serveFlags struct {
122122
Socket string `flag:"socket,default=$GOCACHE_SOCKET,Socket path (required)"`
123-
ModProxy string `flag:"modproxy,default=$GOCACHE_MODPROXY,Module proxy service address ([host]:port)"`
124-
RevProxy string `flag:"revproxy,default=$GOCACHE_REVPROXY,Reverse proxy service address ([host]:port)"`
125-
Targets string `flag:"revproxy-targets,default=$GOCACHE_REVPROXY_TARGETS,Reverse proxy targets (comma-separated)"`
123+
HTTP string `flag:"http,default=$GOCACHE_HTTP,HTTP service address ([host]:port)"`
124+
ModProxy bool `flag:"modproxy,default=$GOCACHE_MODPROXY,Enable a Go module proxy (requires --http)"`
125+
RevProxy string `flag:"revproxy,default=$GOCACHE_REVPROXY,Reverse proxy these hosts (comma-separated)"`
126126
SumDB string `flag:"sumdb,default=$GOCACHE_SUMDB,SumDB servers to proxy for (comma-separated)"`
127127
}
128128

@@ -160,8 +160,30 @@ func runServe(env *command.Env) error {
160160
lst.Close()
161161
}))
162162

163+
// If an HTTP server is enabled, start it up with debug routes.
164+
// If enabled, the module proxy and reverse cache attach to mux below.
165+
var mux *http.ServeMux
166+
if serveFlags.HTTP != "" {
167+
mux = http.NewServeMux()
168+
tsweb.Debugger(mux)
169+
srv := &http.Server{
170+
Addr: serveFlags.HTTP,
171+
Handler: mux,
172+
}
173+
g.Go(srv.ListenAndServe)
174+
vprintf("started HTTP server at %q", serveFlags.HTTP)
175+
g.Go(taskgroup.NoError(func() {
176+
<-ctx.Done()
177+
vprintf("signal received, stopping HTTP service")
178+
srv.Shutdown(context.Background())
179+
}))
180+
}
181+
163182
// If a module proxy is enabled, start it.
164-
if serveFlags.ModProxy != "" {
183+
if serveFlags.ModProxy {
184+
if mux == nil {
185+
return env.Usagef("you must set --http to enable --modproxy")
186+
}
165187
modCachePath := filepath.Join(flags.CacheDir, "module")
166188
if err := os.MkdirAll(modCachePath, 0700); err != nil {
167189
lst.Close()
@@ -190,64 +212,35 @@ func runServe(env *command.Env) error {
190212
Cacher: cacher,
191213
ProxiedSumDBs: []string{"sum.golang.org"}, // default, see below
192214
}
215+
vprintf("enabling Go module proxy")
193216
if serveFlags.SumDB != "" {
194217
proxy.ProxiedSumDBs = strings.Split(serveFlags.SumDB, ",")
195218
vprintf("enabling sum DB proxy for %s", strings.Join(proxy.ProxiedSumDBs, ", "))
196219
}
197220
expvar.Publish("modcache", cacher.Metrics())
198-
199-
// Run an HTTP server exporting the proxy and debug metrics.
200-
mux := http.NewServeMux()
201-
mux.Handle("/", proxy)
202-
tsweb.Debugger(mux)
203-
srv := &http.Server{
204-
Addr: serveFlags.ModProxy,
205-
Handler: mux,
206-
}
207-
g.Go(srv.ListenAndServe)
208-
vprintf("started module proxy at %q", serveFlags.ModProxy)
209-
g.Go(taskgroup.NoError(func() {
210-
<-ctx.Done()
211-
vprintf("signal received, stopping module proxy")
212-
srv.Shutdown(context.Background())
213-
}))
221+
mux.Handle("/mod/", http.StripPrefix("/mod", proxy))
214222
}
215223

216224
// If a reverse proxy is enabled, start it.
217225
if serveFlags.RevProxy != "" {
218-
if serveFlags.Targets == "" {
219-
return env.Usagef("must provide --revproxy-targets when --revproxy is set")
226+
if mux == nil {
227+
return env.Usagef("you must set --http to enable --revproxy")
220228
}
221229
revCachePath := filepath.Join(flags.CacheDir, "revproxy")
222230
if err := os.MkdirAll(revCachePath, 0700); err != nil {
223231
lst.Close()
224232
return fmt.Errorf("create revproxy cache: %w", err)
225233
}
226234
proxy := &revproxy.Server{
227-
Targets: strings.Split(serveFlags.Targets, ","),
235+
Targets: strings.Split(serveFlags.RevProxy, ","),
228236
Local: revCachePath,
229237
S3Client: s3c,
230238
KeyPrefix: path.Join(flags.KeyPrefix, "revproxy"),
231239
Logf: vprintf,
232240
}
233241
expvar.Publish("revcache", proxy.Metrics())
234-
235-
mux := http.NewServeMux()
242+
vprintf("enabling reverse proxy for %s", strings.Join(proxy.Targets, ", "))
236243
mux.Handle("/", proxy)
237-
if serveFlags.ModProxy == "" {
238-
tsweb.Debugger(mux) // attach debugger if --modproxy doesn't already have it
239-
}
240-
srv := &http.Server{
241-
Addr: serveFlags.RevProxy,
242-
Handler: mux,
243-
}
244-
g.Go(srv.ListenAndServe)
245-
vprintf("started reverse proxy at %q for %s", serveFlags.RevProxy, strings.Join(proxy.Targets, ", "))
246-
g.Go(taskgroup.NoError(func() {
247-
<-ctx.Done()
248-
vprintf("signal received, stopping reverse proxy")
249-
srv.Shutdown(context.Background())
250-
}))
251244
}
252245

253246
for {

cmd/go-cache-plugin/go-cache-plugin.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,16 @@ serving directly over stdin/stdout. The "connect" command adapts the direct
4646
interface to this one.
4747
4848
By default, only the build cache is exported via the --socket path.
49-
If --modcache is set, the server also exports a caching module proxy at the
50-
specified address.`,
49+
50+
If --http is set, the server also exports an HTTP server at that address.
51+
By default, this exports only /debug endpoints, including metrics.
52+
When --http is enabled, the following options are available:
53+
54+
- When --modcache is true, the server also exports a caching module proxy at
55+
http://<host>:<port>/mod/.
56+
57+
- When --revproxy is set, the server also hosts a caching reverse proxy for the
58+
specified hosts at http://<host>:<port>/revproxy.`,
5159

5260
SetFlags: command.Flags(flax.MustBind, &serveFlags),
5361
Run: command.Adapt(runServe),

cmd/go-cache-plugin/help.go

Lines changed: 76 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -19,58 +19,8 @@ get credentials from the instnce metadata service; otherwise you will need to
1919
plumb AWS environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY) or
2020
set up a configuration file.
2121
22-
------------------------------------------------------------------------
23-
## To run the plugin directly as a subprocess of the toolchain
24-
25-
export GOCACHEPROG="go-cache-plugin --cache-dir=/tmp/gocache --bucket ..."
26-
go build ...
27-
28-
Alternatively:
29-
30-
export GOCACHE_DIR=/tmp/gocache
31-
export GOCACHE_S3_BUCKET=cache-bucket-name
32-
export GOCACHEPROG=go-cache-plugin
33-
go build ...
34-
35-
In this mode, you must specify the --cache-dir and --bucket settings.
36-
37-
------------------------------------------------------------------------
38-
## To run the plugin as a standalone service
39-
40-
Use the "serve" subcommand:
41-
42-
go-cache-plugin serve \
43-
--cache-dir=/tmp/gocache --bucket=$B \
44-
--socket /var/run/cache.sock
45-
46-
You can then use the "connect" subcommand to wire up the toolchain:
47-
48-
export GOCACHEPROG="go-cache-plugin connect /var/run/cache.sock"
49-
50-
In this mode, the server must have credentials to access to S3, but the
51-
toolchain process does not need AWS credentials.
52-
53-
------------------------------------------------------------------------
54-
## Running a Go module and sum database proxy
55-
56-
With the --modproxy flag, the server will also export an HTTP proxy for the
57-
public Go module proxy (proxy.golang.org) and sum DB (sum.golang.org) at the
58-
given address:
59-
60-
go-cache-plugin serve ... --modproxy=localhost:5970
61-
62-
To use the module proxy, set the standard GOPROXY environment variable:
63-
64-
export GOPROXY=localhost:5970
65-
export GOCACHEPROG="go-cache-plugin connect /var/run/cache.sock"
66-
go build ...
67-
68-
To use the sum DB proxy, set the GOSUMDB environment variable:
69-
70-
export GOSUMDB="sum.golang.org http://localhost:5970/sumdb/sum.golang.org"
71-
72-
See also: https://proxy.golang.org/
73-
`,
22+
See also: "help environment".
23+
Related: "direct-mode", "serve-mode", "module-proxy", "reverse-proxy".`,
7424
},
7525
{
7626
Name: "environment",
@@ -98,9 +48,81 @@ settings can be set via environment variables as well as flags.
9848
Flag (serve) Variable Format Default
9949
--------------------------------------------------------------------
10050
--socket GOCACHE_SOCKET path (required)
101-
--modproxy GOCACHE_MODPROXY [host]:port ""
51+
--http GOCACHE_HTTP [host]:port ""
52+
--modproxy GOCACHE_MODPROXY bool false
53+
--revproxy GOCACHE_REVPROXY host,... ""
10254
--sumdb GOCACHE_SUMDB host,... ""
10355
104-
See also "help configure".`,
56+
See also: "help configure".`,
57+
},
58+
{
59+
Name: "direct-mode",
60+
Help: `Run the plugin directly as a subprocess of the toolchain.
61+
62+
export GOCACHEPROG="go-cache-plugin --cache-dir=/tmp/gocache --bucket ..."
63+
go build ...
64+
65+
Alternatively:
66+
67+
export GOCACHE_DIR=/tmp/gocache
68+
export GOCACHE_S3_BUCKET=cache-bucket-name
69+
export GOCACHEPROG=go-cache-plugin
70+
go build ...
71+
72+
In this mode, you must specify the --cache-dir and --bucket settings.`,
73+
},
74+
{
75+
Name: "serve-mode",
76+
Help: `Run the plugin as a standalone service.
77+
78+
Use the "serve" subcommand:
79+
80+
go-cache-plugin serve \
81+
--cache-dir=/tmp/gocache --bucket=$B \
82+
--socket /var/run/cache.sock
83+
84+
You can then use the "connect" subcommand to wire up the toolchain:
85+
86+
export GOCACHEPROG="go-cache-plugin connect /var/run/cache.sock"
87+
88+
In this mode, the server must have credentials to access to S3, but the
89+
toolchain process does not need AWS credentials.`,
90+
},
91+
{
92+
Name: "module-proxy",
93+
Help: `Run a Go module and sum database proxy.
94+
95+
With the --modproxy flag, the server will also export an HTTP proxy for the
96+
public Go module proxy (proxy.golang.org) and sum DB (sum.golang.org) at the
97+
given address:
98+
99+
go-cache-plugin serve ... --http=localhost:5970 --modproxy
100+
101+
To use the module proxy, set the standard GOPROXY environment variable:
102+
103+
export GOPROXY=localhost:5970
104+
export GOCACHEPROG="go-cache-plugin connect /var/run/cache.sock"
105+
go build ...
106+
107+
To use the sum DB proxy, set the GOSUMDB environment variable:
108+
109+
export GOSUMDB="sum.golang.org http://localhost:5970/sumdb/sum.golang.org"
110+
111+
See also: https://proxy.golang.org/`,
112+
},
113+
{
114+
Name: "reverse-proxy",
115+
Help: `Run a caching reverse proxy.
116+
117+
With the --revproxy flag, the server will also export a caching reverse
118+
proxy for the specified hosts, given as a comma-separated list:
119+
120+
go-cache-plugin serve ... \
121+
--http=localhost:5970 \
122+
--revproxy='api.example.com,*.example2.com'
123+
124+
When this is enabled, you can configure this address as an HTTP proxy:
125+
126+
HTTP_PROXY=localhost:5970 curl https://api.example.com/foo`,
105127
},
106128
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ require (
1313
github.com/creachadair/gocache v0.0.0-20240828204135-c17fe2fd53a6
1414
github.com/creachadair/mds v0.17.1
1515
github.com/creachadair/taskgroup v0.9.1
16+
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da
1617
github.com/goproxy/goproxy v0.17.2
1718
golang.org/x/sync v0.8.0
1819
honnef.co/go/tools v0.5.1
@@ -40,7 +41,6 @@ require (
4041
github.com/beorn7/perks v1.0.1 // indirect
4142
github.com/cespare/xxhash/v2 v2.2.0 // indirect
4243
github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 // indirect
43-
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
4444
github.com/google/uuid v1.6.0 // indirect
4545
github.com/prometheus/client_golang v1.19.1 // indirect
4646
github.com/prometheus/client_model v0.5.0 // indirect

revproxy/cache.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,26 @@ func (s *Server) cacheStoreMemory(hash string, maxAge time.Duration, hdr http.He
9191
s.mcacheMu.Lock()
9292
defer s.mcacheMu.Unlock()
9393
s.mcache.Add(hash, memCacheEntry{
94-
header: hdr,
94+
header: trimCacheHeader(hdr),
9595
body: body,
9696
expires: time.Now().Add(maxAge),
9797
})
9898
}
9999

100+
var keepHeader = []string{
101+
"Cache-Control", "Content-Type", "Date", "Etag",
102+
}
103+
104+
func trimCacheHeader(h http.Header) http.Header {
105+
out := make(http.Header)
106+
for _, name := range keepHeader {
107+
if v := h.Get(name); v != "" {
108+
out.Set(name, v)
109+
}
110+
}
111+
return out
112+
}
113+
100114
// parseCacheDbject parses cached object data to extract the body and headers.
101115
func parseCacheObject(data []byte) ([]byte, http.Header, error) {
102116
hdr, rest, ok := bytes.Cut(data, []byte("\n\n"))

revproxy/internal_test.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
package revproxy
22

33
import (
4-
"net/url"
54
"testing"
65
)
76

87
func TestCheckTarget(t *testing.T) {
9-
s := &Server{
10-
Targets: []string{"foo.com", "*.bar.com"},
11-
}
8+
testTargets := []string{"foo.com", "*.bar.com"}
129
tests := []struct {
1310
input string
1411
want bool
@@ -22,8 +19,7 @@ func TestCheckTarget(t *testing.T) {
2219
{"some.other.bar.com", true},
2320
}
2421
for _, tc := range tests {
25-
u := &url.URL{Host: "localhost", Path: tc.input}
26-
if got := s.checkTarget(u); got != tc.want {
22+
if got := hostMatchesTarget(tc.input, testTargets); got != tc.want {
2723
t.Errorf("Check %q: got %v, want %v", tc.input, got, tc.want)
2824
}
2925
}

0 commit comments

Comments
 (0)