internal/frontend: fix github.com/golang/go redirects

Previously, any URL with the prefix /github.com/golang/go redirected to
their corresponding package page (for example, /std or /cmd/go).
However, this also redirected paths like /github.com/golang/gofrontend.
This bug is now fixed and a test is added.

Fixes golang/go#47957

Change-Id: Iabcc2c57ef9f5365d93911a4971addb8bb5a903d
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/344989
Trust: Julie Qiu <julie@golang.org>
Run-TryBot: Julie Qiu <julie@golang.org>
TryBot-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
diff --git a/internal/frontend/details.go b/internal/frontend/details.go
index a534635..342e387 100644
--- a/internal/frontend/details.go
+++ b/internal/frontend/details.go
@@ -17,6 +17,7 @@
 	"golang.org/x/mod/semver"
 	"golang.org/x/pkgsite/internal"
 	"golang.org/x/pkgsite/internal/middleware"
+	"golang.org/x/pkgsite/internal/stdlib"
 )
 
 // serveDetails handles requests for package/directory/module details pages. It
@@ -33,15 +34,6 @@
 		s.staticPageHandler("homepage", "Home")(w, r)
 		return nil
 	}
-	if strings.HasPrefix(r.URL.Path, "/github.com/golang/go") {
-		urlPath := strings.TrimPrefix(strings.TrimPrefix(r.URL.Path, "/github.com/golang/go"), "/src")
-		if urlPath == "" {
-			http.Redirect(w, r, "/std", http.StatusMovedPermanently)
-			return
-		}
-		http.Redirect(w, r, urlPath, http.StatusMovedPermanently)
-		return
-	}
 	if strings.HasSuffix(r.URL.Path, "/") {
 		http.Redirect(w, r, strings.TrimSuffix(r.URL.Path, "/"), http.StatusMovedPermanently)
 		return
@@ -69,12 +61,30 @@
 	if !isSupportedVersion(urlInfo.fullPath, urlInfo.requestedVersion) {
 		return invalidVersionError(urlInfo.fullPath, urlInfo.requestedVersion)
 	}
+	if urlPath := stdlibRedirectURL(urlInfo.fullPath); urlPath != "" {
+		http.Redirect(w, r, urlPath, http.StatusMovedPermanently)
+		return
+	}
 	if err := checkExcluded(ctx, ds, urlInfo.fullPath); err != nil {
 		return err
 	}
 	return s.serveUnitPage(ctx, w, r, ds, urlInfo)
 }
 
+func stdlibRedirectURL(fullPath string) string {
+	if !strings.HasPrefix(fullPath, stdlib.GitHubRepo) {
+		return ""
+	}
+	if fullPath == stdlib.GitHubRepo || fullPath == stdlib.GitHubRepo+"/src" {
+		return "/std"
+	}
+	urlPath2 := strings.TrimPrefix(strings.TrimPrefix(fullPath, stdlib.GitHubRepo+"/"), "src/")
+	if fullPath == urlPath2 {
+		return ""
+	}
+	return "/" + urlPath2
+}
+
 func invalidVersionError(fullPath, requestedVersion string) error {
 	return &serverError{
 		status: http.StatusBadRequest,
diff --git a/internal/frontend/details_test.go b/internal/frontend/details_test.go
new file mode 100644
index 0000000..a84e8a9
--- /dev/null
+++ b/internal/frontend/details_test.go
@@ -0,0 +1,30 @@
+// Copyright 2021 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 (
+	"testing"
+)
+
+func TestStdlibRedirectURL(t *testing.T) {
+	for _, test := range []struct {
+		path string
+		want string
+	}{
+		{"std", ""},
+		{"cmd/go", ""},
+		{"github.com/golang/go", "/std"},
+		{"github.com/golang/go/src", "/std"},
+		{"github.com/golang/go/src", "/std"},
+		{"github.com/golang/go/cmd/go", "/cmd/go"},
+		{"github.com/golang/go/src/cmd/go", "/cmd/go"},
+		{"github.com/golang/gofrontend", ""},
+		{"github.com/golang/gofrontend/libgo/misc/cgo/frontend/libgo/misc", ""},
+	} {
+		if got := stdlibRedirectURL(test.path); got != test.want {
+			t.Errorf("stdlibRedirectURL(%q) = %q; want = %q", test.path, got, test.want)
+		}
+	}
+}
diff --git a/internal/stdlib/stdlib.go b/internal/stdlib/stdlib.go
index 08fc407..d875773 100644
--- a/internal/stdlib/stdlib.go
+++ b/internal/stdlib/stdlib.go
@@ -184,6 +184,8 @@
 const (
 	GoRepoURL       = "https://go.googlesource.com/go"
 	GoSourceRepoURL = "https://cs.opensource.google/go/go"
+
+	GitHubRepo = "github.com/golang/go"
 )
 
 // UseTestData determines whether to really clone the Go repo, or use