change sumdb proxy impl

pull/211/head
hxzhao527 2022-04-14 15:27:12 +08:00 committed by Curith
parent e03dc0f0bf
commit 450ca75bbd
2 changed files with 174 additions and 53 deletions

View File

@ -6,78 +6,101 @@
package sumdb package sumdb
import ( import (
"context"
"errors"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"net/url" "net/url"
"strings" "strings"
"time"
) )
var enableGoogleSumDB bool var supportedSumDB = map[string][]string{
var supportedSumDB = []string{ "sum.golang.org": {"https://sum.golang.org/", "https://sum.golang.google.cn/"},
"sum.golang.org", "sum.golang.google.cn": {"https://sum.golang.org/", "https://sum.golang.google.cn/"}, // db-name `sum.golang.google.cn` will be replaced in go
"gosum.io", "gosum.io": {"https://gosum.io/"},
} }
func init() { var (
go func() { errSumPathInvalid = errors.New("sumdb request path invalid")
p := "https://sum.golang.org" )
_, err := http.Get(p)
if err == nil {
enableGoogleSumDB = true
}
}()
}
// Handler handles sumdb request // Handler handles sumdb request
// goproxy.io not impl a complete sumdb, just proxy to upstream.
func Handler(w http.ResponseWriter, r *http.Request) { func Handler(w http.ResponseWriter, r *http.Request) {
if strings.HasSuffix(r.URL.Path, "/supported") { whichDB, realPath, err := parsePath(r.URL.Path)
for _, supported := range supportedSumDB { _, supported := supportedSumDB[whichDB]
uri := fmt.Sprintf("/sumdb/%s/supported", supported) if err != nil || !supported {
if r.URL.Path == uri { // if not check the target db,
// curl https://goproxy.io/sumdb/www.google.com will succ
w.WriteHeader(http.StatusGone)
fmt.Fprint(w, "unsupported db")
return
}
// $GOROOT/src/cmd/go/internal/modfetch/sumdb.go@initBase
// > Before accessing any checksum database URL using a proxy, the proxy
// > client should first fetch <proxyURL>/sumdb/<sumdb-name>/supported.
if realPath == "supported" {
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
return return
} }
result := make(chan *http.Response)
ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second) // 2sec maybe enough
defer cancel()
for _, host := range supportedSumDB[whichDB] {
go proxySumdb(ctx, host, realPath, result)
} }
w.WriteHeader(http.StatusGone) select {
return case resp := <-result:
} {
if !enableGoogleSumDB {
sumViaGoproxy(w, r)
return
}
p := "https://" + strings.TrimPrefix(r.URL.Path, "/sumdb/")
proxySumdb(p, w, r)
}
func sumViaGoproxy(w http.ResponseWriter, r *http.Request) {
p := "https://goproxy.io" + r.URL.Path
proxySumdb(p, w, r)
}
func proxySumdb(p string, w http.ResponseWriter, r *http.Request) {
_, err := url.Parse(p)
if err != nil {
w.WriteHeader(http.StatusGone)
fmt.Fprintf(w, err.Error())
return
}
resp, err := http.Get(p)
if err != nil {
w.WriteHeader(http.StatusGone)
fmt.Fprintf(w, err.Error())
return
}
defer resp.Body.Close() defer resp.Body.Close()
w.WriteHeader(resp.StatusCode) w.WriteHeader(resp.StatusCode)
if _, err := io.Copy(w, resp.Body); err != nil { if _, err := io.Copy(w, resp.Body); err != nil {
fmt.Fprintf(w, err.Error()) fmt.Fprint(w, err.Error())
}
}
case <-ctx.Done():
w.WriteHeader(http.StatusGone)
fmt.Fprint(w, ctx.Err().Error())
return return
} }
}
func parsePath(rawPath string) (whichDB, path string, err error) {
parts := strings.SplitN(rawPath, "/", 4)
if len(parts) < 4 {
return "", "", errSumPathInvalid
}
whichDB = parts[2]
path = parts[3]
return return
}
func proxySumdb(ctx context.Context, host, path string, respChan chan<- *http.Response) {
urlPath, err := url.Parse(host)
if err != nil {
return
}
urlPath.Path = path
req, err := http.NewRequestWithContext(ctx, http.MethodGet, urlPath.String(), nil)
if err != nil {
return
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return
}
select {
case <-ctx.Done():
resp.Body.Close()
case respChan <- resp:
}
} }

98
sumdb/handler_test.go Normal file
View File

@ -0,0 +1,98 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package sumdb implements sumdb handler proxy.
package sumdb
import (
"fmt"
"net/http"
"net/http/httptest"
"testing"
)
func TestHandler(t *testing.T) {
if ret := t.Run("supported", testSupported); !ret {
t.Error("supported test failed, stop test")
t.FailNow()
}
t.Run("proxy", testProxy)
}
func testSupported(t *testing.T) {
type TestCase struct {
name string
db string
wantSupported bool
}
tests := []TestCase{
{
name: "sum.golang.org",
db: "sum.golang.org",
wantSupported: true,
},
{
name: "gosum.io",
db: "gosum.io",
wantSupported: true,
},
{
name: "sum.golang.google.cn",
db: "sum.golang.google.cn",
wantSupported: true,
},
{
name: "other",
db: "other",
wantSupported: false,
},
}
for _, testcase := range tests {
t.Run(testcase.name, func(t *testing.T) {
recoder := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("https://goproxy.io/sumdb/%s/supported", testcase.db), nil)
Handler(recoder, req)
resp := recoder.Result()
if support := (resp.StatusCode == http.StatusOK); support != testcase.wantSupported {
t.Errorf("db %s: want %v got %v", testcase.db, testcase.wantSupported, support)
}
resp.Body.Close()
})
}
}
func testProxy(t *testing.T) {
type TestCase struct {
name string
db string
path string
expectSucc bool
}
tests := []TestCase{
{
name: "lookup",
db: "sum.golang.google.cn",
path: "lookup/github.com/goproxyio/goproxy@v1.0.0", // this is a fake testcase
expectSucc: true,
},
}
for _, testcase := range tests {
t.Run(testcase.name, func(t *testing.T) {
recoder := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("https://goproxy.io/sumdb/%s/%s", testcase.db, testcase.path), nil)
Handler(recoder, req)
resp := recoder.Result()
if succ := (resp.StatusCode == http.StatusOK); succ != testcase.expectSucc {
t.Errorf("FETCH from db %s/%s got unexpect http status %d", testcase.db, testcase.path, resp.StatusCode)
return
}
})
}
}