From 21bf115a4c87d514f3d7cebc5aa11979840301db Mon Sep 17 00:00:00 2001 From: lework Date: Thu, 5 Mar 2026 09:38:31 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20SumDB=20=E4=BB=A3=E7=90=86?= =?UTF-8?q?=E6=94=AF=E6=8C=81=EF=BC=8C=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E5=92=8C=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 56 ++++++----- go.mod | 2 +- main.go | 9 ++ sumdb/handler.go | 17 +++- sumdb/handler_test.go | 214 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 273 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 8a99d00..721cff6 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ make ## Started -### Proxy mode +### Proxy mode ```shell ./bin/goproxy -listen=0.0.0.0:80 -cacheDir=/tmp/test @@ -32,7 +32,7 @@ If you run `go get -v pkg` in the proxy machine, you should set a new `GOPATH` w See [`test/get_test.sh`](./test/get_test.sh). -### Router mode +### Router mode ```shell ./bin/goproxy -listen=0.0.0.0:80 -proxy https://goproxy.io @@ -52,7 +52,7 @@ go get +-------> |goproxy| +-------> |goproxy.io| +---> golang.org/x/net router mode proxy mode ``` -In `Router mode`, use the `-exclude` flag to set a glob pattern. The glob will specify what packages should not try to resolve with the value of `-proxy`. Modules which match the `-exclude` pattern will resolve direct to the repo which +In `Router mode`, use the `-exclude` flag to set a glob pattern. The glob will specify what packages should not try to resolve with the value of `-proxy`. Modules which match the `-exclude` pattern will resolve direct to the repo which matches the module path. NOTE: Patterns are matched to the full path specified, not only to the host component. @@ -61,6 +61,16 @@ NOTE: Patterns are matched to the full path specified, not only to the host comp ./bin/goproxy -listen=0.0.0.0:80 -cacheDir=/tmp/test -proxy https://goproxy.io -exclude "*.corp.example.com,rsc.io/private" ``` +### SumDB Proxy + +By default, sumdb (Checksum Database) requests use the same proxy host specified by the `-proxy` flag. You can specify a different sumdb proxy using the `-sumdbProxy` flag: + +```shell +./bin/goproxy -listen=0.0.0.0:80 -proxy https://goproxy.io -sumdbProxy https://goproxy.cn +``` + +When the `-sumdbProxy` flag is set, all sumdb requests (including `sum.golang.org`, `sum.golang.google.cn`, and `gosum.io`) will be proxied through the specified host. + ### Private module authentication Some private modules are gated behind `git` authentication. To resolve this, you can force git to rewrite the URL with a personal access token present for auth @@ -77,7 +87,7 @@ This can be done for other git providers as well, following the same pattern docker run -d -p80:8081 goproxy/goproxy ``` -Use the -v flag to persisting the proxy module data (change ___cacheDir___ to your own dir): +Use the -v flag to persisting the proxy module data (change **_cacheDir_** to your own dir): ``` docker run -d -p80:8081 -v cacheDir:/go goproxy/goproxy @@ -108,27 +118,27 @@ spec: app: goproxy spec: containers: - - args: - - -proxy - - https://goproxy.io - - -listen - - 0.0.0.0:8081 - - -cacheDir - - /tmp/test - - -exclude - - github.com/my-org/* - image: goproxy/goproxy - name: goproxy - ports: - - containerPort: 8081 - volumeMounts: - - mountPath: /tmp/test + - args: + - -proxy + - https://goproxy.io + - -listen + - 0.0.0.0:8081 + - -cacheDir + - /tmp/test + - -exclude + - github.com/my-org/* + image: goproxy/goproxy name: goproxy + ports: + - containerPort: 8081 + volumeMounts: + - mountPath: /tmp/test + name: goproxy volumes: - - emptyDir: - medium: Memory - sizeLimit: 500Mi - name: goproxy + - emptyDir: + medium: Memory + sizeLimit: 500Mi + name: goproxy ``` Deployment (with gitconfig secret): diff --git a/go.mod b/go.mod index 7b464fc..6478584 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/goproxyio/goproxy/v2 -go 1.12 +go 1.26 require ( github.com/goproxyio/windows v0.0.0-20191126033816-f4a809841617 diff --git a/main.go b/main.go index b2b6f0e..df11074 100644 --- a/main.go +++ b/main.go @@ -34,6 +34,7 @@ import ( "time" "github.com/goproxyio/goproxy/v2/proxy" + "github.com/goproxyio/goproxy/v2/sumdb" "github.com/prometheus/client_golang/prometheus/promhttp" "golang.org/x/mod/module" @@ -43,12 +44,14 @@ var downloadRoot string var listen, promListen string var cacheDir string var proxyHost string +var sumdbProxy string var excludeHost string var cacheExpire time.Duration func init() { flag.StringVar(&excludeHost, "exclude", "", "exclude host pattern, you can exclude internal Git services") flag.StringVar(&proxyHost, "proxy", "", "next hop proxy for Go Modules, recommend use https://goproxy.io") + flag.StringVar(&sumdbProxy, "sumdbProxy", "", "sumdb proxy host, default use proxy value") flag.StringVar(&cacheDir, "cacheDir", "", "Go Modules cache dir, default is $GOPATH/pkg/mod/cache/download") flag.StringVar(&listen, "listen", "0.0.0.0:8081", "service listen address") flag.DurationVar(&cacheExpire, "cacheExpire", 5*time.Minute, "Go Modules cache expiration (min), default is 5 min") @@ -79,6 +82,12 @@ func main() { log.SetFlags(0) var handle http.Handler + + if sumdbProxy != "" { + log.Printf("SumDBProxy %s\n", sumdbProxy) + sumdb.SetSumdbProxy(sumdbProxy) + } + if proxyHost != "" { log.Printf("ProxyHost %s\n", proxyHost) if excludeHost != "" { diff --git a/sumdb/handler.go b/sumdb/handler.go index 321011c..2ad96b0 100644 --- a/sumdb/handler.go +++ b/sumdb/handler.go @@ -10,6 +10,7 @@ import ( "errors" "fmt" "io" + "log" "net/http" "net/url" "strings" @@ -86,7 +87,9 @@ func proxySumdb(ctx context.Context, host, path string, respChan chan<- *http.Re if err != nil { return } - urlPath.Path = path + + urlPath.Path = strings.TrimSuffix(urlPath.Path, "/") + "/" + path + log.Printf("[sumdb] proxy request to: %s\n", urlPath.String()) req, err := http.NewRequestWithContext(ctx, http.MethodGet, urlPath.String(), nil) if err != nil { @@ -94,6 +97,7 @@ func proxySumdb(ctx context.Context, host, path string, respChan chan<- *http.Re } resp, err := http.DefaultClient.Do(req) if err != nil { + log.Printf("[sumdb] proxy request error: %v\n", err) return } @@ -104,3 +108,14 @@ func proxySumdb(ctx context.Context, host, path string, respChan chan<- *http.Re } } + +func SetSumdbProxy(proxyHost string) { + if proxyHost == "" { + return + } + proxyHost = strings.TrimSuffix(proxyHost, "/") + for dbName := range supportedSumDB { + proxyURL := proxyHost + "/sumdb/" + dbName + "/" + supportedSumDB[dbName] = []string{proxyURL} + } +} \ No newline at end of file diff --git a/sumdb/handler_test.go b/sumdb/handler_test.go index 287745f..4fd5e74 100644 --- a/sumdb/handler_test.go +++ b/sumdb/handler_test.go @@ -96,3 +96,217 @@ func testProxy(t *testing.T) { }) } } + +func TestParsePath(t *testing.T) { + type TestCase struct { + name string + rawPath string + wantWhichDB string + wantPath string + wantErr bool + } + + tests := []TestCase{ + { + name: "valid path with sum.golang.org", + rawPath: "/sumdb/sum.golang.org/supported", + wantWhichDB: "sum.golang.org", + wantPath: "supported", + wantErr: false, + }, + { + name: "valid path with lookup", + rawPath: "/sumdb/sum.golang.org/lookup/github.com/test@v1.0.0", + wantWhichDB: "sum.golang.org", + wantPath: "lookup/github.com/test@v1.0.0", + wantErr: false, + }, + { + name: "valid path with gosum.io", + rawPath: "/sumdb/gosum.io/supported", + wantWhichDB: "gosum.io", + wantPath: "supported", + wantErr: false, + }, + { + name: "invalid path - too few parts", + rawPath: "/sumdb/sum.golang.org", + wantWhichDB: "", + wantPath: "", + wantErr: true, + }, + { + name: "invalid path - empty", + rawPath: "", + wantWhichDB: "", + wantPath: "", + wantErr: true, + }, + { + name: "invalid path - only root", + rawPath: "/", + wantWhichDB: "", + wantPath: "", + wantErr: true, + }, + { + name: "invalid path - only sumdb", + rawPath: "/sumdb", + wantWhichDB: "", + wantPath: "", + wantErr: true, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + whichDB, path, err := parsePath(tc.rawPath) + + if tc.wantErr { + if err == nil { + t.Errorf("parsePath(%q) expected error, got nil", tc.rawPath) + } + if err != errSumPathInvalid { + t.Errorf("parsePath(%q) expected errSumPathInvalid, got %v", tc.rawPath, err) + } + return + } + + if err != nil { + t.Errorf("parsePath(%q) unexpected error: %v", tc.rawPath, err) + return + } + + if whichDB != tc.wantWhichDB { + t.Errorf("parsePath(%q) whichDB = %q, want %q", tc.rawPath, whichDB, tc.wantWhichDB) + } + + if path != tc.wantPath { + t.Errorf("parsePath(%q) path = %q, want %q", tc.rawPath, path, tc.wantPath) + } + }) + } +} + +func TestSetSumdbProxy(t *testing.T) { + // Save original state to restore after tests + originalSupportedSumDB := make(map[string][]string) + for k, v := range supportedSumDB { + originalSupportedSumDB[k] = append([]string(nil), v...) + } + defer func() { + supportedSumDB = originalSupportedSumDB + }() + + type TestCase struct { + name string + proxyHost string + wantURLs map[string]string + } + + tests := []TestCase{ + { + name: "empty proxy host - should not modify", + proxyHost: "", + wantURLs: map[string]string{ + "sum.golang.org": "https://sum.golang.org/", + "sum.golang.google.cn": "https://sum.golang.org/", + "gosum.io": "https://gosum.io/", + }, + }, + { + name: "set proxy host without trailing slash", + proxyHost: "https://goproxy.cn", + wantURLs: map[string]string{ + "sum.golang.org": "https://goproxy.cn/sumdb/sum.golang.org/", + "sum.golang.google.cn": "https://goproxy.cn/sumdb/sum.golang.google.cn/", + "gosum.io": "https://goproxy.cn/sumdb/gosum.io/", + }, + }, + { + name: "set proxy host with trailing slash", + proxyHost: "https://goproxy.cn/", + wantURLs: map[string]string{ + "sum.golang.org": "https://goproxy.cn/sumdb/sum.golang.org/", + "sum.golang.google.cn": "https://goproxy.cn/sumdb/sum.golang.google.cn/", + "gosum.io": "https://goproxy.cn/sumdb/gosum.io/", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + // Reset to original state before each test + supportedSumDB = make(map[string][]string) + for k, v := range originalSupportedSumDB { + supportedSumDB[k] = append([]string(nil), v...) + } + + SetSumdbProxy(tc.proxyHost) + + if tc.proxyHost == "" { + // When proxyHost is empty, verify nothing changed + for dbName := range supportedSumDB { + if len(supportedSumDB[dbName]) != len(originalSupportedSumDB[dbName]) { + t.Errorf("SetSumdbProxy(\"\") modified %s unexpectedly", dbName) + } + } + return + } + + // Verify all DB names have been updated + for dbName, expectedURL := range tc.wantURLs { + urls, exists := supportedSumDB[dbName] + if !exists { + t.Errorf("SetSumdbProxy() removed db %s", dbName) + continue + } + if len(urls) != 1 { + t.Errorf("SetSumdbProxy() db %s has %d URLs, want 1", dbName, len(urls)) + continue + } + if urls[0] != expectedURL { + t.Errorf("SetSumdbProxy() db %s = %q, want %q", dbName, urls[0], expectedURL) + } + } + }) + } +} + +func TestHandlerInvalidPath(t *testing.T) { + type TestCase struct { + name string + path string + expectedStatus int + expectedBody string + } + + tests := []TestCase{ + { + name: "path too short", + path: "/sumdb/sum.golang.org", + expectedStatus: http.StatusGone, + expectedBody: "unsupported db", + }, + { + name: "unsupported database", + path: "/sumdb/unsupported.db.com/supported", + expectedStatus: http.StatusGone, + expectedBody: "unsupported db", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + recorder := httptest.NewRecorder() + req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("https://goproxy.io%s", tc.path), nil) + Handler(recorder, req) + + resp := recorder.Result() + if resp.StatusCode != tc.expectedStatus { + t.Errorf("Handler() status = %d, want %d", resp.StatusCode, tc.expectedStatus) + } + resp.Body.Close() + }) + } +}