зеркало из https://github.com/golang/pkgsite.git
discovery/internal/proxyclient: create client for module proxy
The discovery/internal/proxyclient package is created to interact with a module proxy. See `go help goproxy` for proxy protocol details. The following structs are introduced: - proxyclient.Client - proxyclient.VersionInfo The following exported functions are implemented: - New(rawurl string) (*Client, error) - GetInfo(name, version string) (*VersionInfo, error) discovery/internal/testdata is moved to discovery/internal/fetch/testdata. Change-Id: I3b0fa8dcf95f8dbbc39519640adbf199c1e61efb Reviewed-on: https://team-review.git.corp.google.com/c/414334 Reviewed-by: Andrew Bonventre <andybons@google.com>
This commit is contained in:
Родитель
88e3a2059e
Коммит
1e7ae17793
|
@ -0,0 +1,58 @@
|
|||
// 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 proxyclient
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// A Client is used by the fetch service to communicate with a module
|
||||
// proxy. It handles all methods defined by go help goproxy.
|
||||
// TODO(julieqiu): Implement GetList, GetMod, and GetZip.
|
||||
type Client struct {
|
||||
url string // URL of the module proxy web server
|
||||
}
|
||||
|
||||
// A VersionInfo contains metadata about a given version of a module.
|
||||
type VersionInfo struct {
|
||||
Version string
|
||||
Time time.Time
|
||||
}
|
||||
|
||||
// cleanURL trims the rawurl of trailing slashes.
|
||||
func cleanURL(rawurl string) string {
|
||||
return strings.TrimRight(rawurl, "/"), nil
|
||||
}
|
||||
|
||||
// New constructs a *Client using the provided rawurl, which is expected to
|
||||
// be an absolute URI that can be directly passed to http.Get.
|
||||
func New(rawurl string) *Client {
|
||||
return &Client{url: cleanURL(rawurl)}, nil
|
||||
}
|
||||
|
||||
// infoURL constructs a url for a GET request to $GOPROXY/<module>/@v/list.
|
||||
func (c *Client) infoURL(name, version string) string {
|
||||
return fmt.Sprintf("%s/%s/@v/%s.info", c.url, name, version)
|
||||
}
|
||||
|
||||
// GetInfo makes a GET request to $GOPROXY/<module>/@v/<version>.info and
|
||||
// transforms that data into a *VersionInfo.
|
||||
func (c *Client) GetInfo(name, version string) (*VersionInfo, error) {
|
||||
r, err := http.Get(c.infoURL(name, version))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
var v VersionInfo
|
||||
if err = json.NewDecoder(r.Body).Decode(&v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &v, nil
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
// 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 proxyclient
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type testCase struct {
|
||||
proxy *httptest.Server
|
||||
client *Client
|
||||
}
|
||||
|
||||
func setupTestCase(t *testing.T) (func(t *testing.T), *testCase) {
|
||||
proxy := httptest.NewServer(http.FileServer(http.Dir("testdata/modproxy/proxy")))
|
||||
tc := testCase{
|
||||
proxy: proxy,
|
||||
client: New(proxy.URL),
|
||||
}
|
||||
|
||||
fn := func(t *testing.T) {
|
||||
proxy.Close()
|
||||
}
|
||||
return fn, &tc
|
||||
}
|
||||
|
||||
func TestCleanURL(t *testing.T) {
|
||||
for raw, expected := range map[string]string{
|
||||
"http://localhost:7000/index": "http://localhost:7000/index",
|
||||
"http://host.com/": "http://host.com",
|
||||
"http://host.com///": "http://host.com",
|
||||
} {
|
||||
got, err := cleanURL(raw)
|
||||
if err != nil {
|
||||
t.Errorf("cleanURL(%q) error: %v", raw, err)
|
||||
}
|
||||
if got != expected {
|
||||
t.Errorf("cleanURL(%q) = %q, want %q", raw, got, expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCleanURLErrors(t *testing.T) {
|
||||
for _, raw := range []string{
|
||||
"localhost:7000/index",
|
||||
"host.com",
|
||||
"",
|
||||
} {
|
||||
_, err := cleanURL(raw)
|
||||
expectedErr := "is an invalid url"
|
||||
if err == nil {
|
||||
t.Errorf("cleanURL(%q) error = %v, want %q", raw, err, expectedErr)
|
||||
}
|
||||
if !strings.Contains(err.Error(), expectedErr) {
|
||||
t.Errorf("cleanURL(%q) error = %v, want %q", raw, err, expectedErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetInfo(t *testing.T) {
|
||||
teardownTestCase, testCase := setupTestCase(t)
|
||||
defer teardownTestCase(t)
|
||||
|
||||
name := "my/module"
|
||||
version := "v1.0.0"
|
||||
info, err := testCase.proxyClient.GetInfo(name, version)
|
||||
if err != nil {
|
||||
t.Errorf("GetInfo(%q, %q) error: %v", name, version, err)
|
||||
}
|
||||
|
||||
if info.Version != version {
|
||||
t.Errorf("VersionInfo.Version for GetInfo(%q, %q) = %q, want %q", name, version, info.Version, version)
|
||||
}
|
||||
|
||||
expectedTime := time.Date(2019, 1, 30, 0, 0, 0, 0, time.UTC)
|
||||
if info.Time != expectedTime {
|
||||
t.Errorf("VersionInfo.Time for GetInfo(%q, %q) = %v, want %v", name, version, info.Time, expectedTime)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetInfoVersionDoesNotExist(t *testing.T) {
|
||||
teardownTestCase, testCase := setupTestCase(t)
|
||||
defer teardownTestCase(t)
|
||||
|
||||
name := "my/module"
|
||||
version := "v3.0.0"
|
||||
info, _ := testCase.proxyClient.GetInfo(name, version)
|
||||
if info != nil {
|
||||
t.Errorf("GetInfo(%q, %q) = %v, want %v", name, version, info, nil)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
// 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.
|
||||
|
||||
// modproxy runs a local module proxy for testing. It implements the Module
|
||||
// proxy protocol described at `go help goproxy` by serving files stored at
|
||||
// ./proxy. The following modules are supported by this proxy:
|
||||
// my/module v1.0.0
|
||||
// my/module v1.1.0
|
||||
// my/module/2 v12.0.0
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
var proxyURL, _ = filepath.Abs("proxy")
|
||||
|
||||
func main() {
|
||||
http.Handle("/", http.FileServer(http.Dir(proxyURL)))
|
||||
|
||||
addr := ":7000"
|
||||
log.Println(fmt.Sprintf("Listening on http://localhost%s", addr))
|
||||
log.Fatal(http.ListenAndServe(addr, nil))
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
// 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 main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const proxyURL = "./proxy"
|
||||
|
||||
func main() {
|
||||
http.Handle("/", http.FileServer(http.Dir(proxyURL)))
|
||||
|
||||
log.Fatal(http.ListenAndServe(":8080", nil))
|
||||
}
|
Загрузка…
Ссылка в новой задаче