internal/frontend: redirect github.com/golang requests

When a request is made to github.com/golang, redirect it to the
corresponding stdlib or x repo page, instead of returning a 404.

Change-Id: I8f3d3bf69de2a73998cf9c3d33542c2ebb975e14
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/282533
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/server.go b/internal/frontend/server.go
index f841446..eace0ac 100644
--- a/internal/frontend/server.go
+++ b/internal/frontend/server.go
@@ -137,6 +137,19 @@
 	handle("/license-policy", s.licensePolicyHandler())
 	handle("/about", http.RedirectHandler("https://go.dev/about", http.StatusFound))
 	handle("/badge/", http.HandlerFunc(s.badgeHandler))
+	handle("/github.com/golang/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		suffix := strings.TrimPrefix(r.URL.Path, "/github.com/golang")
+		if !strings.HasPrefix(r.URL.Path, "/github.com/golang/go") {
+			http.Redirect(w, r, "/golang.org/x"+suffix, http.StatusMovedPermanently)
+			return
+		}
+		urlPath := strings.TrimPrefix(strings.TrimPrefix(suffix, "/go"), "/src")
+		if urlPath == "" {
+			http.Redirect(w, r, "/std", http.StatusMovedPermanently)
+			return
+		}
+		http.Redirect(w, r, urlPath, http.StatusMovedPermanently)
+	}))
 	handle("/C", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		// Package "C" is a special case: redirect to /cmd/cgo.
 		// (This is what golang.org/C does.)
diff --git a/internal/frontend/server_test.go b/internal/frontend/server_test.go
index 30c3b11..e2e96e6 100644
--- a/internal/frontend/server_test.go
+++ b/internal/frontend/server_test.go
@@ -627,6 +627,48 @@
 			wantLocation:   "/cmd/cgo",
 		},
 		{
+			name:           "github golang std",
+			urlPath:        "/github.com/golang/go",
+			wantStatusCode: http.StatusMovedPermanently,
+			wantLocation:   "/std",
+		},
+		{
+			name:           "github golang std src",
+			urlPath:        "/github.com/golang/go/src",
+			wantStatusCode: http.StatusMovedPermanently,
+			wantLocation:   "/std",
+		},
+		{
+			name:           "github golang time",
+			urlPath:        "/github.com/golang/go/time",
+			wantStatusCode: http.StatusMovedPermanently,
+			wantLocation:   "/time",
+		},
+		{
+			name:           "github golang time src",
+			urlPath:        "/github.com/golang/go/src/time",
+			wantStatusCode: http.StatusMovedPermanently,
+			wantLocation:   "/time",
+		},
+		{
+			name:           "github golang x tools repo",
+			urlPath:        "/github.com/golang/tools",
+			wantStatusCode: http.StatusMovedPermanently,
+			wantLocation:   "/golang.org/x/tools",
+		},
+		{
+			name:           "github golang x tools go packages",
+			urlPath:        "/github.com/golang/tools/go/packages",
+			wantStatusCode: http.StatusMovedPermanently,
+			wantLocation:   "/golang.org/x/tools/go/packages",
+		},
+		{
+			name:           "github golang x tools gopls",
+			urlPath:        "/github.com/golang/tools/gopls",
+			wantStatusCode: http.StatusMovedPermanently,
+			wantLocation:   "/golang.org/x/tools/gopls",
+		},
+		{
 			name:           "static",
 			urlPath:        "/static/",
 			wantStatusCode: http.StatusOK,
diff --git a/internal/testing/integration/frontend_doc_render_test.go b/internal/testing/integration/frontend_doc_render_test.go
index b05fa51..2c61951 100644
--- a/internal/testing/integration/frontend_doc_render_test.go
+++ b/internal/testing/integration/frontend_doc_render_test.go
@@ -27,10 +27,10 @@
 	defer postgres.ResetTestDB(testDB, t)
 
 	m := &proxy.Module{
-		ModulePath: "github.com/golang/fdoc",
+		ModulePath: "github.com/foo/fdoc",
 		Version:    "v1.2.3",
 		Files: map[string]string{
-			"go.mod":  "module github.com/golang/fdoc",
+			"go.mod":  "module github.com/foo/fdoc",
 			"LICENSE": testhelper.MITLicense,
 			"file.go": `
 					// Package fdoc is a test of frontend doc rendering.