mirror of https://github.com/golang/go.git
cmd/go: case-encode versions as well as module paths in files, URLs
While writing the GOPROXY docs it occurred to me that versions can contain upper-case letters as well. The docs therefore say that versions are case-encoded the same as paths in the proxy protocol (and therefore in the cache as well). Make it so. Change-Id: Ibc0c4af0192a4af251e5dd6f2d36cda7e529099a Reviewed-on: https://go-review.googlesource.com/124795 Reviewed-by: Bryan C. Mills <bcmills@google.com>
This commit is contained in:
parent
4f1f503373
commit
a59f443897
|
|
@ -46,7 +46,11 @@ func CachePath(m module.Version, suffix string) (string, error) {
|
|||
if module.CanonicalVersion(m.Version) != m.Version {
|
||||
return "", fmt.Errorf("non-canonical module version %q", m.Version)
|
||||
}
|
||||
return filepath.Join(dir, m.Version+"."+suffix), nil
|
||||
encVer, err := module.EncodeVersion(m.Version)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.Join(dir, encVer+"."+suffix), nil
|
||||
}
|
||||
|
||||
func DownloadDir(m module.Version) (string, error) {
|
||||
|
|
@ -63,7 +67,11 @@ func DownloadDir(m module.Version) (string, error) {
|
|||
if module.CanonicalVersion(m.Version) != m.Version {
|
||||
return "", fmt.Errorf("non-canonical module version %q", m.Version)
|
||||
}
|
||||
return filepath.Join(SrcMod, enc+"@"+m.Version), nil
|
||||
encVer, err := module.EncodeVersion(m.Version)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.Join(SrcMod, enc+"@"+encVer), nil
|
||||
}
|
||||
|
||||
// A cachingRepo is a cache around an underlying Repo,
|
||||
|
|
|
|||
|
|
@ -163,7 +163,11 @@ func (p *proxyRepo) latest() (*RevInfo, error) {
|
|||
|
||||
func (p *proxyRepo) Stat(rev string) (*RevInfo, error) {
|
||||
var data []byte
|
||||
err := webGetBytes(p.url+"/@v/"+pathEscape(rev)+".info", &data)
|
||||
encRev, err := module.EncodeVersion(rev)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = webGetBytes(p.url+"/@v/"+pathEscape(encRev)+".info", &data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -191,7 +195,11 @@ func (p *proxyRepo) Latest() (*RevInfo, error) {
|
|||
|
||||
func (p *proxyRepo) GoMod(version string) ([]byte, error) {
|
||||
var data []byte
|
||||
err := webGetBytes(p.url+"/@v/"+pathEscape(version)+".mod", &data)
|
||||
encVer, err := module.EncodeVersion(version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = webGetBytes(p.url+"/@v/"+pathEscape(encVer)+".mod", &data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -200,7 +208,11 @@ func (p *proxyRepo) GoMod(version string) ([]byte, error) {
|
|||
|
||||
func (p *proxyRepo) Zip(version string, tmpdir string) (tmpfile string, err error) {
|
||||
var body io.ReadCloser
|
||||
err = webGetBody(p.url+"/@v/"+pathEscape(version)+".zip", &body)
|
||||
encVer, err := module.EncodeVersion(version)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
err = webGetBody(p.url+"/@v/"+pathEscape(encVer)+".zip", &body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -433,8 +433,22 @@ func EncodePath(path string) (encoding string, err error) {
|
|||
return "", err
|
||||
}
|
||||
|
||||
return encodeString(path)
|
||||
}
|
||||
|
||||
// EncodeVersion returns the safe encoding of the given module version.
|
||||
// Versions are allowed to be in non-semver form but must be valid file names
|
||||
// and not contain exclamation marks.
|
||||
func EncodeVersion(v string) (encoding string, err error) {
|
||||
if err := checkElem(v, true); err != nil || strings.Contains(v, "!") {
|
||||
return "", fmt.Errorf("disallowed version string %q", v)
|
||||
}
|
||||
return encodeString(v)
|
||||
}
|
||||
|
||||
func encodeString(s string) (encoding string, err error) {
|
||||
haveUpper := false
|
||||
for _, r := range path {
|
||||
for _, r := range s {
|
||||
if r == '!' || r >= utf8.RuneSelf {
|
||||
// This should be disallowed by CheckPath, but diagnose anyway.
|
||||
// The correctness of the encoding loop below depends on it.
|
||||
|
|
@ -446,11 +460,11 @@ func EncodePath(path string) (encoding string, err error) {
|
|||
}
|
||||
|
||||
if !haveUpper {
|
||||
return path, nil
|
||||
return s, nil
|
||||
}
|
||||
|
||||
var buf []byte
|
||||
for _, r := range path {
|
||||
for _, r := range s {
|
||||
if 'A' <= r && r <= 'Z' {
|
||||
buf = append(buf, '!', byte(r+'a'-'A'))
|
||||
} else {
|
||||
|
|
@ -461,19 +475,45 @@ func EncodePath(path string) (encoding string, err error) {
|
|||
}
|
||||
|
||||
// DecodePath returns the module path of the given safe encoding.
|
||||
// It fails if the encoding is invalid.
|
||||
// It fails if the encoding is invalid or encodes an invalid path.
|
||||
func DecodePath(encoding string) (path string, err error) {
|
||||
path, ok := decodeString(encoding)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("invalid module path encoding %q", encoding)
|
||||
}
|
||||
if err := CheckPath(path); err != nil {
|
||||
return "", fmt.Errorf("invalid module path encoding %q: %v", encoding, err)
|
||||
}
|
||||
return path, nil
|
||||
}
|
||||
|
||||
// DecodeVersion returns the version string for the given safe encoding.
|
||||
// It fails if the encoding is invalid or encodes an invalid version.
|
||||
// Versions are allowed to be in non-semver form but must be valid file names
|
||||
// and not contain exclamation marks.
|
||||
func DecodeVersion(encoding string) (v string, err error) {
|
||||
v, ok := decodeString(encoding)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("invalid version encoding %q", encoding)
|
||||
}
|
||||
if err := checkElem(v, true); err != nil {
|
||||
return "", fmt.Errorf("disallowed version string %q", v)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func decodeString(encoding string) (string, bool) {
|
||||
var buf []byte
|
||||
|
||||
bang := false
|
||||
for _, r := range encoding {
|
||||
if r >= utf8.RuneSelf {
|
||||
goto BadEncoding
|
||||
return "", false
|
||||
}
|
||||
if bang {
|
||||
bang = false
|
||||
if r < 'a' || 'z' < r {
|
||||
goto BadEncoding
|
||||
return "", false
|
||||
}
|
||||
buf = append(buf, byte(r+'A'-'a'))
|
||||
continue
|
||||
|
|
@ -483,19 +523,12 @@ func DecodePath(encoding string) (path string, err error) {
|
|||
continue
|
||||
}
|
||||
if 'A' <= r && r <= 'Z' {
|
||||
goto BadEncoding
|
||||
return "", false
|
||||
}
|
||||
buf = append(buf, byte(r))
|
||||
}
|
||||
if bang {
|
||||
goto BadEncoding
|
||||
return "", false
|
||||
}
|
||||
path = string(buf)
|
||||
if err := CheckPath(path); err != nil {
|
||||
return "", fmt.Errorf("invalid module path encoding %q: %v", encoding, err)
|
||||
}
|
||||
return path, nil
|
||||
|
||||
BadEncoding:
|
||||
return "", fmt.Errorf("invalid module path encoding %q", encoding)
|
||||
return string(buf), true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,13 +78,18 @@ func readModList() {
|
|||
if i < 0 {
|
||||
continue
|
||||
}
|
||||
enc := strings.Replace(name[:i], "_", "/", -1)
|
||||
path, err := module.DecodePath(enc)
|
||||
encPath := strings.Replace(name[:i], "_", "/", -1)
|
||||
path, err := module.DecodePath(encPath)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "go proxy_test: %v", err)
|
||||
continue
|
||||
}
|
||||
encVers := name[i+1:]
|
||||
vers, err := module.DecodeVersion(encVers)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "go proxy_test: %v", err)
|
||||
continue
|
||||
}
|
||||
vers := name[i+1:]
|
||||
modList = append(modList, module.Version{Path: path, Version: vers})
|
||||
}
|
||||
}
|
||||
|
|
@ -132,7 +137,13 @@ func proxyHandler(w http.ResponseWriter, r *http.Request) {
|
|||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
vers, ext := file[:i], file[i+1:]
|
||||
encVers, ext := file[:i], file[i+1:]
|
||||
vers, err := module.DecodeVersion(encVers)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "go proxy_test: %v", err)
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
if codehost.AllHex(vers) {
|
||||
var best string
|
||||
|
|
@ -239,9 +250,14 @@ func readArchive(path, vers string) *txtar.Archive {
|
|||
fmt.Fprintf(os.Stderr, "go proxy: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
encVers, err := module.EncodeVersion(vers)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "go proxy: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
prefix := strings.Replace(enc, "/", "_", -1)
|
||||
name := filepath.Join(cmdGoDir, "testdata/mod", prefix+"_"+vers+".txt")
|
||||
name := filepath.Join(cmdGoDir, "testdata/mod", prefix+"_"+encVers+".txt")
|
||||
a := archiveCache.Do(name, func() interface{} {
|
||||
a, err := txtar.ParseFile(name)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
rsc.io/quote@v2.0.0 && cp mod/rsc.io_quote_v0.0.0-20180709153244-fd906ed3b100.txt mod/rsc.io_quote_v2.0.0.txt
|
||||
rsc.io/QUOTE v1.5.2
|
||||
|
||||
-- .mod --
|
||||
module rsc.io/QUOTE
|
||||
|
|
|
|||
|
|
@ -0,0 +1,88 @@
|
|||
rsc.io/QUOTE v1.5.3-PRE (sigh)
|
||||
|
||||
-- .mod --
|
||||
module rsc.io/QUOTE
|
||||
|
||||
require rsc.io/quote v1.5.2
|
||||
-- .info --
|
||||
{"Version":"v1.5.3-PRE","Name":"","Short":"","Time":"2018-07-15T16:25:34Z"}
|
||||
-- go.mod --
|
||||
module rsc.io/QUOTE
|
||||
|
||||
require rsc.io/quote v1.5.2
|
||||
-- QUOTE/quote.go --
|
||||
// Copyright 2018 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 QUOTE COLLECTS LOUD SAYINGS.
|
||||
package QUOTE
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"rsc.io/quote"
|
||||
)
|
||||
|
||||
// HELLO RETURNS A GREETING.
|
||||
func HELLO() string {
|
||||
return strings.ToUpper(quote.Hello())
|
||||
}
|
||||
|
||||
// GLASS RETURNS A USEFUL PHRASE FOR WORLD TRAVELERS.
|
||||
func GLASS() string {
|
||||
return strings.ToUpper(quote.GLASS())
|
||||
}
|
||||
|
||||
// GO RETURNS A GO PROVERB.
|
||||
func GO() string {
|
||||
return strings.ToUpper(quote.GO())
|
||||
}
|
||||
|
||||
// OPT RETURNS AN OPTIMIZATION TRUTH.
|
||||
func OPT() string {
|
||||
return strings.ToUpper(quote.OPT())
|
||||
}
|
||||
-- QUOTE/quote_test.go --
|
||||
// Copyright 2018 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 QUOTE
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func init() {
|
||||
os.Setenv("LC_ALL", "en")
|
||||
}
|
||||
|
||||
func TestHELLO(t *testing.T) {
|
||||
hello := "HELLO, WORLD"
|
||||
if out := HELLO(); out != hello {
|
||||
t.Errorf("HELLO() = %q, want %q", out, hello)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGLASS(t *testing.T) {
|
||||
glass := "I CAN EAT GLASS AND IT DOESN'T HURT ME."
|
||||
if out := GLASS(); out != glass {
|
||||
t.Errorf("GLASS() = %q, want %q", out, glass)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGO(t *testing.T) {
|
||||
go1 := "DON'T COMMUNICATE BY SHARING MEMORY, SHARE MEMORY BY COMMUNICATING."
|
||||
if out := GO(); out != go1 {
|
||||
t.Errorf("GO() = %q, want %q", out, go1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOPT(t *testing.T) {
|
||||
opt := "IF A PROGRAM IS TOO SLOW, IT MUST HAVE A LOOP."
|
||||
if out := OPT(); out != opt {
|
||||
t.Errorf("OPT() = %q, want %q", out, opt)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
rsc.io/quote@v2.0.0 && cp mod/rsc.io_quote_v0.0.0-20180709153244-fd906ed3b100.txt mod/rsc.io_quote_v2.0.0.txt
|
||||
rsc.io/quote@v2.0.0
|
||||
|
||||
-- .mod --
|
||||
module "rsc.io/quote"
|
||||
|
|
|
|||
|
|
@ -9,5 +9,12 @@ go list -f 'DIR {{.Dir}} DEPS {{.Deps}}' rsc.io/QUOTE/QUOTE
|
|||
stdout 'DEPS.*rsc.io/quote'
|
||||
stdout 'DIR.*!q!u!o!t!e'
|
||||
|
||||
go get rsc.io/QUOTE@v1.5.3-PRE
|
||||
go list -m all
|
||||
stdout '^rsc.io/QUOTE v1.5.3-PRE'
|
||||
|
||||
go list -f '{{.Dir}}' rsc.io/QUOTE/QUOTE
|
||||
stdout '!q!u!o!t!e@v1.5.3-!p!r!e'
|
||||
|
||||
-- go.mod --
|
||||
module x
|
||||
|
|
|
|||
Loading…
Reference in New Issue