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 frontend

import (
	
	
	
	
	
	
	
	
	

	
	
	
	
	
	
	
	
)
errUnitNotFoundWithoutFetch returns a 404 with instructions to the user on how to manually fetch the package. No fetch button is provided. This is used for very large modules or modules that previously 500ed.
var errUnitNotFoundWithoutFetch = &serverError{
	status: http.StatusNotFound,
	epage: &errorPage{
		messageTemplate: template.MakeTrustedTemplate(`
					    <h3 class="Error-message">{{.StatusText}}</h3>
					    <p class="Error-message">Check that you entered the URL correctly or try fetching it following the
                        <a href="/about#adding-a-package">instructions here</a>.</p>`),
		MessageData: struct{ StatusText string }{http.StatusText(http.StatusNotFound)},
	},
}
servePathNotFoundPage serves a 404 page for the requested path, or redirects the user to an appropriate location.
func ( *Server) ( http.ResponseWriter,  *http.Request,
	 internal.DataSource, , ,  string) ( error) {
	defer derrors.Wrap(&, "servePathNotFoundPage(w, r, %q, %q)", , )

	,  := .(*postgres.DB)
	if ! {
		return proxydatasourceNotSupportedErr()
	}
	 := .Context()

	if stdlib.Contains() {
		var  string
		,  = stdlibPathForShortcut(, , )
Log the error, but prefer a "path not found" error for a better user experience.
			log.Error(, )
		}
		if  != "" {
			http.Redirect(, , fmt.Sprintf("/%s", ), http.StatusFound)
			return
		}
		return &serverError{status: http.StatusNotFound}
	}

	,  := previousFetchStatusAndResponse(, , , , )
If an error occurred, it means that we have never tried to fetch this path before or an error occurred when we tried to gather data about this 404. If the latter, log the error. In either case, give the user the option to fetch that path.
		if !errors.Is(, derrors.NotFound) {
			log.Error(, )
		}
		return pathNotFoundError(, )
	}
If we've reached this point, we know that we've seen this path before. Show a relevant page or redirect the use based on the previous fetch response.
The redirectPath and the fullpath are the same. Do not redirect to avoid ending up in a loop.
			return errUnitNotFoundWithoutFetch
		}
		,  := .GetVersionMap(, .goModPath, internal.LatestVersion)
		if ( != nil && !errors.Is(, derrors.NotFound)) ||
We attempted to fetch the canonical module path before and were not successful. Do not redirect this request.
			return errUnitNotFoundWithoutFetch
		}
		 := constructUnitURL(.goModPath, .goModPath, internal.LatestVersion)
		cookie.Set(, cookie.AlternativeModuleFlash, , )
		http.Redirect(, , , http.StatusFound)
		return nil
	case http.StatusInternalServerError:
		return pathNotFoundError(, )
	default:
		if  := githubPathRedirect();  != "" {
			http.Redirect(, , , http.StatusFound)
			return
		}
If a module has a status of 404, but s.taskIDChangeInterval has passed, allow the module to be refetched.
		if .status == http.StatusNotFound && time.Since(.updatedAt) > .taskIDChangeInterval {
			return pathNotFoundError(, )
		}
Redirect to the search result page for an empty directory that is above nested modules. See https://golang.org/issue/43725 for context.
		,  := .GetNestedModules(, )
		if  == nil && len() > 0 {
			http.Redirect(, , "/search?q="+url.QueryEscape(), http.StatusFound)
			return nil
		}
		return &serverError{
			status: .status,
			epage: &errorPage{
				messageTemplate: uncheckedconversions.TrustedTemplateFromStringKnownToSatisfyTypeContract(`
					    <h3 class="Error-message">{{.StatusText}}</h3>
					    <p class="Error-message">` + html.UnescapeString(.responseText) + `</p>`),
				MessageData: struct{  string }{http.StatusText(.status)},
			},
		}
	}
}
githubRegexp is regex to match a GitHub URL scheme containing a "/blob" or "/tree" element.
var githubRegexp = regexp.MustCompile(`(blob|tree)(/[^/]+)?`)

func ( string) string {
	 := strings.Split(, "/")
	if len() <= 3 || [0] != "github.com" {
		return ""
	}
	 := strings.Split(, "/"+githubRegexp.FindString())
	if len() != 2 {
		return ""
	}
	 := [0]
	if [1] != "" {
		 = [0] + [1]
	}
	return constructUnitURL(, , internal.LatestVersion)
}
pathNotFoundError returns a page with an option on how to add a package or module to the site.
func (,  string) error {
	if !isSupportedVersion(, ) {
		return invalidVersionError(, )
	}
	if stdlib.Contains() {
		return &serverError{status: http.StatusNotFound}
	}
	 := 
	if  != internal.LatestVersion {
		 = fmt.Sprintf("%s@%s", , )
	}
	return &serverError{
		status: http.StatusNotFound,
		epage: &errorPage{
			templateName: "fetch.tmpl",
			MessageData:  ,
		},
	}
}
previousFetchStatusAndResponse returns the fetch result from a previous fetch of the fullPath and requestedVersion.
func ( context.Context,  *postgres.DB,
	, ,  string) ( *fetchResult,  error) {
	defer derrors.Wrap(&, "previousFetchStatusAndResponse(w, r, %q, %q)", , )
Get all candidate module paths for this path.
	,  := modulePathsToFetch(, , , )
	if  != nil {
		return nil, 
Check if a row exists in the version_map table for the longest candidate path and version. If we have not fetched the path before, a derrors.NotFound will be returned.
	,  := .GetVersionMap(, [0], )
	if  != nil {
		return nil, 
If the row has been fetched before, and the result was either a 490, 491, or 5xx, return that result, since it is a final state.
	if  != nil &&
		(.Status >= 500 ||
			.Status == derrors.ToStatus(derrors.AlternativeModule) ||
			.Status == derrors.ToStatus(derrors.BadModule)) {
		return resultFromFetchRequest([]*fetchResult{
			{
				modulePath: .ModulePath,
				goModPath:  .GoModPath,
				status:     .Status,
				err:        errors.New(.Error),
			},
		}, , )
	}
Check if the unit path exists at a higher major version. For example, my.module might not exist, but my.module/v3 might. Similarly, my.module/foo might not exist, but my.module/v3/foo might. In either case, the user will be redirected to the highest major version of the path. Do not bother to look for a specific version if this case. If my.module/foo@v2.1.0 was requested, and my.module/foo/v2 exists, just return the latest version of my.module/foo/v2. Only redirect if the majPath returned is different from the fullPath, and the majPath is not at v1. We don't want to redirect my.module/foo/v3 to my.module/foo, or my.module/foo@v1.5.2 to my.module/foo@v1.0.0.
	, ,  := .GetLatestMajorPathForV1Path(, )
	if  != nil &&  != derrors.NotFound {
		return nil, 
	}
	if  !=  &&  != 1 &&  != "" {
		return &fetchResult{
			modulePath: ,
			goModPath:  ,
			status:     http.StatusFound,
		}, nil
	}
	,  := .GetVersionMaps(, , )
	if  != nil {
		return nil, 
	}
	var  []*fetchResult
	for ,  := range  {
		 := fetchResultFromVersionMap()
		 = append(, )
		if .Status == http.StatusOK || .Status == 290 {
			.err = errPathDoesNotExistInModule
		}
	}
	if len() == 0 {
		return nil, derrors.NotFound
	}
	return resultFromFetchRequest(, , )
}

func ( *internal.VersionMap) *fetchResult {
	var  error
	if .Error != "" {
		 = errors.New(.Error)
	}
	return &fetchResult{
		modulePath: .ModulePath,
		goModPath:  .GoModPath,
		status:     .Status,
		updatedAt:  .UpdatedAt,
		err:        ,
	}