github.com/zxy12/go_duplicate_112_new@v0.0.0-20200807091221-747231827200/src/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  }