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 proxydatasource implements an internal.DataSource backed solely by a proxy instance.
package proxydatasource

import (
	
	
	
	
	
	
	
	

	
	
	
	
	
	
)

var _ internal.DataSource = (*DataSource)(nil)
New returns a new direct proxy datasource.
func ( *proxy.Client) *DataSource {
	return newDataSource(, source.NewClient(1*time.Minute))
}

func ( *proxy.Client) *DataSource {
	return newDataSource(, source.NewClientForTesting())
}

func ( *proxy.Client,  *source.Client) *DataSource {
	return &DataSource{
		proxyClient:          ,
		sourceClient:         ,
		versionCache:         make(map[versionKey]*versionEntry),
		modulePathToVersions: make(map[string][]string),
		packagePathToModules: make(map[string][]string),
		bypassLicenseCheck:   false,
	}
}
NewBypassingLicenseCheck returns a new direct proxy datasource that bypasses license checks. That means all data will be returned for non-redistributable modules, packages and directories.
DataSource implements the frontend.DataSource interface, by querying a module proxy directly and caching the results in memory.
Use an extremely coarse lock for now - mu guards all maps below. The assumption is that this will only be used for local development.
map of modulePath -> versions, with versions sorted in semver order
map of package path -> modules paths containing it, with module paths sorted by descending length
versionEntry holds the result of a call to worker.FetchModule.
getModule retrieves a version from the cache, or failing that queries and processes the version from the proxy.
func ( *DataSource) ( context.Context, ,  string,  internal.BuildContext) ( *internal.Module,  error) {
	defer derrors.Wrap(&, "getModule(%q, %q)", , )

	 := versionKey{, }
	.mu.Lock()
	defer .mu.Unlock()
	if ,  := .versionCache[];  {
		return .module, .err
	}
	 := fetch.FetchModule(, , , .proxyClient, .sourceClient)
	defer .Defer()
	 := .Module
	if  != nil {
		if .bypassLicenseCheck {
			.IsRedistributable = true
			for ,  := range .Packages() {
				.IsRedistributable = true
			}
		} else {
			.RemoveNonRedistributableData()
Use the go.mod file at the raw latest version to fill in deprecation and retraction information.
		,  := fetch.LatestModuleVersions(, , .proxyClient, nil)
		if  != nil {
			.Error = 
		} else {
			.PopulateModuleInfo(&.ModuleInfo)
		}
	}

	if .Error != nil {
		if !errors.Is(.Err(), context.Canceled) {
			.versionCache[] = &versionEntry{module: , err: .Error}
		}
		return nil, .Error
	}

	.versionCache[] = &versionEntry{module: , err: }
Since we hold the lock and missed the cache, we can assume that we have never seen this module version. Therefore the following insert-and-sort preserves uniqueness of versions in the module version list.
	 := append(.modulePathToVersions[], )
	sort.Slice(, func(,  int) bool {
		return semver.Compare([], []) < 0
	})
	.modulePathToVersions[] = 
Unlike the above, we don't know at this point whether or not we've seen this module path for this particular package before. Therefore, we need to be a bit more careful and check that it is new. To do this, we can leverage the invariant that module paths in packagePathToModules are kept sorted in descending order of length.
	for ,  := range .Packages() {
		var (
			   int
			  string
			 = .packagePathToModules[.Path]
		)
		for ,  = range  {
			if len() <= len() {
				break
			}
		}
		if  !=  {
			.packagePathToModules[.Path] = append([:], append([]string{}, [:]...)...)
		}
	}
	return , nil
}
findModule finds the longest module path containing the given package path, using the given finder func and iteratively testing parent directories of the import path. It performs no testing as to whether the specified module version that was found actually contains a package corresponding to pkgPath.
func ( *DataSource) ( context.Context,  string,  string) ( string,  *proxy.VersionInfo,  error) {
	defer derrors.Wrap(&, "findModule(%q, ...)", )
	 = strings.TrimLeft(, "/")
	for ,  := range internal.CandidateModulePaths() {
		,  := .proxyClient.Info(, , )
		if errors.Is(, derrors.NotFound) {
			continue
		}
		if  != nil {
			return "", nil, 
		}
		return , , nil
	}
	return "", nil, fmt.Errorf("unable to find module: %w", derrors.NotFound)
}
getUnit returns information about a unit.
func ( *DataSource) ( context.Context, , ,  string,  internal.BuildContext) ( *internal.Unit,  error) {
	var  *internal.Module
	,  = .getModule(, , , )
	if  != nil {
		return nil, 
	}
	for ,  := range .Units {
		if .Path ==  {
			return , nil
		}
	}
	return nil, fmt.Errorf("%q missing from module %s: %w", , .ModulePath, derrors.NotFound)
}
GetLatestInfo returns latest information for unitPath and modulePath.
func ( *DataSource) ( context.Context, ,  string,  *internal.UnitMeta) ( internal.LatestInfo,  error) {
	defer derrors.Wrap(&, "GetLatestInfo(ctx, %q, %q)", , )

	if  == nil {
		,  = .GetUnitMeta(, , internal.UnknownModulePath, internal.LatestVersion)
		if  != nil {
			return , 
		}
	}
	.MinorVersion = .Version
	.MinorModulePath = .ModulePath

	.MajorModulePath, .MajorUnitPath,  = .getLatestMajorVersion(, , )
	if  != nil {
		return , 
Do not try to discover whether the unit is in the latest minor version; assume it is.
	.UnitExistsAtMinor = true
	return , nil
}
getLatestMajorVersion returns the latest module path and the full package path of the latest version found in the proxy by iterating through vN versions. This function does not attempt to find whether the full path exists in the new major version.
We are checking if the full path is valid so that we can forward the error if not.
	 := internal.SeriesPathForModule()
	,  := .proxyClient.Info(, , internal.LatestVersion)
	if  != nil {
		return "", "", 
	}
Converting version numbers to integers may cause an overflow, as version numbers need not fit into machine integers. While using Atoi is wrong, for it to fail, the version number must reach a value higher than at least 2^31, which is unlikely.
	,  := strconv.Atoi(strings.TrimPrefix(semver.Major(.Version), "v"))
	if  != nil {
		return "", "", 
	}
	++
We start checking versions from "/v2" or higher, since v1 and v0 versions don't have a major version at the end of the modulepath.
	if  < 2 {
		 = 2
	}

	for  := ; ; ++ {
		 := fmt.Sprintf("%s/v%d", , )

		,  := .proxyClient.Info(, , internal.LatestVersion)
		if errors.Is(, derrors.NotFound) {
			if  == 2 {
				return , , nil
			}
			 := fmt.Sprintf("%s/v%d", , -1)
			return , , nil
		}
		if  != nil {
			return "", "", 
		}
	}