github.com/megatontech/mynoteforgo@v0.0.0-20200507084910-5d0c6ea6e890/源码/cmd/go/proxy_test.go (about) 1 // Copyright 2018 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package main_test 6 7 import ( 8 "archive/zip" 9 "bytes" 10 "encoding/json" 11 "flag" 12 "fmt" 13 "io/ioutil" 14 "log" 15 "net" 16 "net/http" 17 "os" 18 "path/filepath" 19 "strings" 20 "sync" 21 "testing" 22 23 "cmd/go/internal/modfetch" 24 "cmd/go/internal/modfetch/codehost" 25 "cmd/go/internal/module" 26 "cmd/go/internal/par" 27 "cmd/go/internal/semver" 28 "cmd/go/internal/txtar" 29 ) 30 31 var ( 32 proxyAddr = flag.String("proxy", "", "run proxy on this network address instead of running any tests") 33 proxyURL string 34 ) 35 36 var proxyOnce sync.Once 37 38 // StartProxy starts the Go module proxy running on *proxyAddr (like "localhost:1234") 39 // and sets proxyURL to the GOPROXY setting to use to access the proxy. 40 // Subsequent calls are no-ops. 41 // 42 // The proxy serves from testdata/mod. See testdata/mod/README. 43 func StartProxy() { 44 proxyOnce.Do(func() { 45 fmt.Fprintf(os.Stderr, "go test proxy starting\n") 46 readModList() 47 addr := *proxyAddr 48 if addr == "" { 49 addr = "localhost:0" 50 } 51 l, err := net.Listen("tcp", addr) 52 if err != nil { 53 log.Fatal(err) 54 } 55 *proxyAddr = l.Addr().String() 56 proxyURL = "http://" + *proxyAddr + "/mod" 57 fmt.Fprintf(os.Stderr, "go test proxy running at GOPROXY=%s\n", proxyURL) 58 go func() { 59 log.Fatalf("go proxy: http.Serve: %v", http.Serve(l, http.HandlerFunc(proxyHandler))) 60 }() 61 }) 62 } 63 64 var modList []module.Version 65 66 func readModList() { 67 infos, err := ioutil.ReadDir("testdata/mod") 68 if err != nil { 69 log.Fatal(err) 70 } 71 for _, info := range infos { 72 name := info.Name() 73 if !strings.HasSuffix(name, ".txt") { 74 continue 75 } 76 name = strings.TrimSuffix(name, ".txt") 77 i := strings.LastIndex(name, "_v") 78 if i < 0 { 79 continue 80 } 81 encPath := strings.ReplaceAll(name[:i], "_", "/") 82 path, err := module.DecodePath(encPath) 83 if err != nil { 84 fmt.Fprintf(os.Stderr, "go proxy_test: %v\n", err) 85 continue 86 } 87 encVers := name[i+1:] 88 vers, err := module.DecodeVersion(encVers) 89 if err != nil { 90 fmt.Fprintf(os.Stderr, "go proxy_test: %v\n", err) 91 continue 92 } 93 modList = append(modList, module.Version{Path: path, Version: vers}) 94 } 95 } 96 97 var zipCache par.Cache 98 99 // proxyHandler serves the Go module proxy protocol. 100 // See the proxy section of https://research.swtch.com/vgo-module. 101 func proxyHandler(w http.ResponseWriter, r *http.Request) { 102 if !strings.HasPrefix(r.URL.Path, "/mod/") { 103 http.NotFound(w, r) 104 return 105 } 106 path := strings.TrimPrefix(r.URL.Path, "/mod/") 107 i := strings.Index(path, "/@v/") 108 if i < 0 { 109 http.NotFound(w, r) 110 return 111 } 112 enc, file := path[:i], path[i+len("/@v/"):] 113 path, err := module.DecodePath(enc) 114 if err != nil { 115 fmt.Fprintf(os.Stderr, "go proxy_test: %v\n", err) 116 http.NotFound(w, r) 117 return 118 } 119 if file == "list" { 120 n := 0 121 for _, m := range modList { 122 if m.Path == path && !modfetch.IsPseudoVersion(m.Version) { 123 if err := module.Check(m.Path, m.Version); err == nil { 124 fmt.Fprintf(w, "%s\n", m.Version) 125 n++ 126 } 127 } 128 } 129 if n == 0 { 130 http.NotFound(w, r) 131 } 132 return 133 } 134 135 i = strings.LastIndex(file, ".") 136 if i < 0 { 137 http.NotFound(w, r) 138 return 139 } 140 encVers, ext := file[:i], file[i+1:] 141 vers, err := module.DecodeVersion(encVers) 142 if err != nil { 143 fmt.Fprintf(os.Stderr, "go proxy_test: %v\n", err) 144 http.NotFound(w, r) 145 return 146 } 147 148 if codehost.AllHex(vers) { 149 var best string 150 // Convert commit hash (only) to known version. 151 // Use latest version in semver priority, to match similar logic 152 // in the repo-based module server (see modfetch.(*codeRepo).convert). 153 for _, m := range modList { 154 if m.Path == path && semver.Compare(best, m.Version) < 0 { 155 var hash string 156 if modfetch.IsPseudoVersion(m.Version) { 157 hash = m.Version[strings.LastIndex(m.Version, "-")+1:] 158 } else { 159 hash = findHash(m) 160 } 161 if strings.HasPrefix(hash, vers) || strings.HasPrefix(vers, hash) { 162 best = m.Version 163 } 164 } 165 } 166 if best != "" { 167 vers = best 168 } 169 } 170 171 a := readArchive(path, vers) 172 if a == nil { 173 fmt.Fprintf(os.Stderr, "go proxy: no archive %s %s\n", path, vers) 174 http.Error(w, "cannot load archive", 500) 175 return 176 } 177 178 switch ext { 179 case "info", "mod": 180 want := "." + ext 181 for _, f := range a.Files { 182 if f.Name == want { 183 w.Write(f.Data) 184 return 185 } 186 } 187 188 case "zip": 189 type cached struct { 190 zip []byte 191 err error 192 } 193 c := zipCache.Do(a, func() interface{} { 194 var buf bytes.Buffer 195 z := zip.NewWriter(&buf) 196 for _, f := range a.Files { 197 if strings.HasPrefix(f.Name, ".") { 198 continue 199 } 200 var zipName string 201 if strings.HasPrefix(f.Name, "/") { 202 zipName = f.Name[1:] 203 } else { 204 zipName = path + "@" + vers + "/" + f.Name 205 } 206 zf, err := z.Create(zipName) 207 if err != nil { 208 return cached{nil, err} 209 } 210 if _, err := zf.Write(f.Data); err != nil { 211 return cached{nil, err} 212 } 213 } 214 if err := z.Close(); err != nil { 215 return cached{nil, err} 216 } 217 return cached{buf.Bytes(), nil} 218 }).(cached) 219 220 if c.err != nil { 221 fmt.Fprintf(os.Stderr, "go proxy: %v\n", c.err) 222 http.Error(w, c.err.Error(), 500) 223 return 224 } 225 w.Write(c.zip) 226 return 227 228 } 229 http.NotFound(w, r) 230 } 231 232 func findHash(m module.Version) string { 233 a := readArchive(m.Path, m.Version) 234 if a == nil { 235 return "" 236 } 237 var data []byte 238 for _, f := range a.Files { 239 if f.Name == ".info" { 240 data = f.Data 241 break 242 } 243 } 244 var info struct{ Short string } 245 json.Unmarshal(data, &info) 246 return info.Short 247 } 248 249 var archiveCache par.Cache 250 251 var cmdGoDir, _ = os.Getwd() 252 253 func readArchive(path, vers string) *txtar.Archive { 254 enc, err := module.EncodePath(path) 255 if err != nil { 256 fmt.Fprintf(os.Stderr, "go proxy: %v\n", err) 257 return nil 258 } 259 encVers, err := module.EncodeVersion(vers) 260 if err != nil { 261 fmt.Fprintf(os.Stderr, "go proxy: %v\n", err) 262 return nil 263 } 264 265 prefix := strings.ReplaceAll(enc, "/", "_") 266 name := filepath.Join(cmdGoDir, "testdata/mod", prefix+"_"+encVers+".txt") 267 a := archiveCache.Do(name, func() interface{} { 268 a, err := txtar.ParseFile(name) 269 if err != nil { 270 if testing.Verbose() || !os.IsNotExist(err) { 271 fmt.Fprintf(os.Stderr, "go proxy: %v\n", err) 272 } 273 a = nil 274 } 275 return a 276 }).(*txtar.Archive) 277 return a 278 }