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 fetch provides a way to fetch modules from a proxy.
package fetch

import (
	
	
	
	
	
	
	

	
	
	
	
	
	
	
	
)
A goPackage is a group of one or more Go source files with the same package header. Packages are part of a module.
v1path is the package path of a package with major version 1 in a given series.
	v1path string
	docs   []*internal.Documentation // doc for different build contexts
	err    error                     // non-fatal error when loading the package (e.g. documentation is too large)
}
extractPackagesFromZip returns a slice of packages from the module zip r. It matches against the given licenses to determine the subset of licenses that applies to each package. The second return value says whether any packages are "incomplete," meaning that they contained .go files but couldn't be processed due to current limitations of this site. The limitations are: * a maximum file size (MaxFileSize) * the particular set of build contexts we consider (goEnvs) * whether the import path is valid.
func ( context.Context, ,  string,  *zip.Reader,  *licenses.Detector,  *source.Info) ( []*goPackage,  []*internal.PackageVersionState,  error) {
	defer derrors.Wrap(&, "extractPackagesFromZip(ctx, %q, %q, r, d)", , )
	,  := trace.StartSpan(, "fetch.extractPackagesFromZip")
	defer .End()
	defer func() {
The package processing code performs some sanity checks along the way. None of the panics should occur, but if they do, we want to log them and be able to find them. So, convert internal panics to internal errors here.
			 = fmt.Errorf("internal panic: %v\n\n%s", , debug.Stack())
		}
	}()
The high-level approach is to split the processing of the zip file into two phases: 1. loop over all files, looking at file metadata only 2. process all files by reading their contents During phase 1, we populate the dirs map for each directory that contains at least one .go file.

modulePrefix is the "<module>@<resolvedVersion>/" prefix that all files are expected to have according to the zip archive layout specification at the bottom of https://golang.org/cmd/go/#hdr-Module_proxy_protocol.
		 = moduleVersionDir(, ) + "/"
dirs is the set of directories with at least one .go file, to be populated during phase 1 and used during phase 2. The map key is the directory path, with the modulePrefix trimmed. The map value is a slice of all .go files, and no other files.
		 = make(map[string][]*zip.File)
modInfo contains all the module information a package in the module needs to render its documentation, to be populated during phase 1 and used during phase 2.
		 = &godoc.ModuleInfo{
			ModulePath:      ,
			ResolvedVersion: ,
			ModulePackages:  make(map[string]bool),
		}
incompleteDirs tracks directories for which we have incomplete information, due to a problem processing one of the go files contained therein. We use this so that a single unprocessable package does not prevent processing of other packages in the module.
		       = make(map[string]bool)
		 = []*internal.PackageVersionState{}
	)
Phase 1. Loop over zip files preemptively and check for problems that can be detected by looking at metadata alone. We'll be looking at file contents starting with phase 2 only, only after we're sure this phase passed without errors.
	for ,  := range .File {
While "go mod download" will never put a directory in a zip, anyone can serve their own zips. Example: go.felesatra.moe/binpack@v0.1.0. Directory entries are harmless, so we just ignore them.
			continue
		}
Well-formed module zips have all files under modulePrefix.
			return nil, nil, fmt.Errorf("expected file to have prefix %q; got = %q: %w",
				, .Name, errMalformedZip)
		}
		 := path.Dir(.Name[len():])
We already know this directory cannot be processed, so skip.
			continue
		}
		 := path.Join(, )
File is in a directory we're not looking to process at this time, so skip it.
			continue
		}
We care about .go files only.
			continue
It's possible to have a Go package in a directory that does not result in a valid import path. That package cannot be imported, but that may be fine if it's a main package, intended to built and run from that directory. Example: https://github.com/postmannen/go-learning/blob/master/concurrency/01-sending%20numbers%20and%20receving%20numbers%20from%20a%20channel/main.go We're not set up to handle invalid import paths, so skip these packages.
		if  := module.CheckImportPath();  != nil {
			[] = true
			 = append(, &internal.PackageVersionState{
				ModulePath:  ,
				PackagePath: ,
				Version:     ,
				Status:      derrors.ToStatus(derrors.PackageBadImportPath),
				Error:       .Error(),
			})
			continue
		}
		if .UncompressedSize64 > MaxFileSize {
			[] = true
			 := derrors.ToStatus(derrors.PackageMaxFileSizeLimitExceeded)
			 := fmt.Sprintf("Unable to process %s: file size %d exceeds max limit %d",
				.Name, .UncompressedSize64, MaxFileSize)
			 = append(, &internal.PackageVersionState{
				ModulePath:  ,
				PackagePath: ,
				Version:     ,
				Status:      ,
				Error:       ,
			})
			continue
		}
		[] = append([], )
		if len() > maxPackagesPerModule {
			return nil, nil, fmt.Errorf("%d packages found in %q; exceeds limit %d for maxPackagePerModule", len(), , maxPackagesPerModule)
		}
	}
	for  := range  {
		.ModulePackages[path.Join(, )] = true
	}
Phase 2. If we got this far, the file metadata was okay. Start reading the file contents now to extract information about Go packages.
	var  []*goPackage
	for ,  := range  {
Something went wrong when processing this directory, so we skip.
			log.Infof(, "Skipping %q because it is incomplete", )
			continue
		}

		var (
			 error
			 string
		)
		,  := loadPackage(, , , , )
		if  := (*BadPackageError)(nil); errors.As(, &) {
			[] = true
			 = derrors.PackageInvalidContents
			 = .Error()
		} else if  != nil {
			return nil, nil, fmt.Errorf("unexpected error loading package: %v", )
		}
		var  string
No package.
There were go files, but no build contexts matched them.
				[] = true
				 = derrors.PackageBuildContextNotSupported
			}
			 = path.Join(, )
		} else {
			if errors.Is(.err, godoc.ErrTooLarge) {
				 = derrors.PackageDocumentationHTMLTooLarge
				 = .err.Error()
ErrTooLarge is the only valid value of pkg.err.
				return nil, nil, fmt.Errorf("bad package error for %s: %v", .path, .err)
			}
			if  != nil { //  should only be nil for tests
				,  := .PackageInfo()
				.isRedistributable = 
				for ,  := range  {
					.licenseMeta = append(.licenseMeta, .Metadata)
				}
			}
			 = append(, )
			 = .path
		}
		 = append(, &internal.PackageVersionState{
			ModulePath:  ,
			PackagePath: ,
			Version:     ,
			Status:      derrors.ToStatus(),
			Error:       ,
		})
	}
	if len() == 0 {
		return nil, , ErrModuleContainsNoPackages
	}
	return , , nil
}
ignoredByGoTool reports whether the given import path corresponds to a directory that would be ignored by the go tool. The logic of the go tool for ignoring directories is documented at https://golang.org/cmd/go/#hdr-Package_lists_and_patterns: Directory and file names that begin with "." or "_" are ignored by the go tool, as are directories named "testdata". However, even though `go list` and other commands that take package wildcards will ignore these, they can still be imported and used in working Go programs. We continue to ignore the "." and "testdata" cases, but we've seen valid Go packages with "_", so we accept those.
func ( string) bool {
	for ,  := range strings.Split(, "/") {
		if strings.HasPrefix(, ".") ||  == "testdata" {
			return true
		}
	}
	return false
}
isVendored reports whether the given import path corresponds to a Go package that is inside a vendor directory. The logic for what is considered a vendor directory is documented at https://golang.org/cmd/go/#hdr-Vendor_Directories.
func ( string) bool {
	return strings.HasPrefix(, "vendor/") ||
		strings.Contains(, "/vendor/")