gddo-server: add utm_source to pkg.go.dev redirect URLs

This will reliably indicate to pkg.go.dev that a request is due
to the automatic redirect logic.

Additional logic is added for more accurate mapping of
godoc.org -> pkg.go.dev URLs.

Updates golang/go#37099

Change-Id: Iba380a3b5951d50576c17711990d0ba87857b926
Reviewed-on: https://go-review.googlesource.com/c/gddo/+/224659
Reviewed-by: Julie Qiu <julie@golang.org>
diff --git a/gddo-server/main.go b/gddo-server/main.go
index 795df07..5154bf4 100644
--- a/gddo-server/main.go
+++ b/gddo-server/main.go
@@ -1095,16 +1095,9 @@
 	return req.FormValue("utm_source") == "backtogodoc"
 }
 
-var gddoToPkgGoDevRequest = map[string]string{
-	"/-/about": "/about",
-	"/-/go":    "/std",
-}
-
 // pkgGoDevRedirectHandler redirects requests from godoc.org to pkg.go.dev,
 // based on whether a cookie is set for pkggodev-redirect. The cookie
-// can be turned on/off using a query param. It determines which path to
-// direct to by checking if a path is mapped in gddoToPkgGoDevRequest, and
-// if not redirecting to the same path that was used for the godoc.org request.
+// can be turned on/off using a query param.
 func pkgGoDevRedirectHandler(f func(http.ResponseWriter, *http.Request) error) func(http.ResponseWriter, *http.Request) error {
 	return func(w http.ResponseWriter, r *http.Request) error {
 		if userReturningFromPkgGoDev(r) {
@@ -1138,17 +1131,45 @@
 			return f(w, r)
 		}
 
-		path, ok := gddoToPkgGoDevRequest[r.URL.Path]
-		if !ok {
-			path = r.URL.Path
-		}
-
-		nextUrl := url.URL{Scheme: "https", Host: pkgGoDevHost, Path: path}
-		http.Redirect(w, r, nextUrl.String(), http.StatusFound)
+		http.Redirect(w, r, pkgGoDevURL(r.URL).String(), http.StatusFound)
 		return nil
 	}
 }
 
+func pkgGoDevURL(godocURL *url.URL) *url.URL {
+	u := &url.URL{Scheme: "https", Host: pkgGoDevHost}
+	q := url.Values{"utm_source": []string{"godoc"}}
+
+	switch godocURL.Path {
+	case "/-/go":
+		u.Path = "/std"
+		q.Add("tab", "packages")
+	case "/-/about":
+		u.Path = "/about"
+	case "/":
+		if qparam := godocURL.Query().Get("q"); qparam != "" {
+			u.Path = "/search"
+			q.Set("q", qparam)
+		} else {
+			u.Path = "/"
+		}
+	default:
+		{
+			u.Path = godocURL.Path
+			if _, ok := godocURL.Query()["imports"]; ok {
+				q.Set("tab", "imports")
+			} else if _, ok := godocURL.Query()["importers"]; ok {
+				q.Set("tab", "importedby")
+			} else {
+				q.Set("tab", "doc")
+			}
+		}
+	}
+
+	u.RawQuery = q.Encode()
+	return u
+}
+
 func main() {
 	ctx := context.Background()
 	v, err := loadConfig(ctx, os.Args)
diff --git a/gddo-server/main_test.go b/gddo-server/main_test.go
index 269030a..9e9d3e8 100644
--- a/gddo-server/main_test.go
+++ b/gddo-server/main_test.go
@@ -9,6 +9,7 @@
 import (
 	"net/http"
 	"net/http/httptest"
+	"net/url"
 	"testing"
 
 	"github.com/google/go-cmp/cmp"
@@ -51,7 +52,7 @@
 		{
 			name:                "test pkggodev-redirect param is on",
 			url:                 "http://godoc.org/net/http?redirect=on",
-			wantLocationHeader:  "https://pkg.go.dev/net/http",
+			wantLocationHeader:  "https://pkg.go.dev/net/http?tab=doc&utm_source=godoc",
 			wantSetCookieHeader: "pkggodev-redirect=on; Path=/",
 			wantStatusCode:      http.StatusFound,
 		},
@@ -81,7 +82,7 @@
 			name:                "pkggodev-redirect enabled cookie should redirect",
 			url:                 "http://godoc.org/net/http",
 			cookie:              &http.Cookie{Name: "pkggodev-redirect", Value: "on"},
-			wantLocationHeader:  "https://pkg.go.dev/net/http",
+			wantLocationHeader:  "https://pkg.go.dev/net/http?tab=doc&utm_source=godoc",
 			wantSetCookieHeader: "",
 			wantStatusCode:      http.StatusFound,
 		},
@@ -120,6 +121,49 @@
 	}
 }
 
+func TestGodoc(t *testing.T) {
+	testCases := []struct {
+		from, to string
+	}{
+		{
+			from: "https://godoc.org/-/about",
+			to:   "https://pkg.go.dev/about?utm_source=godoc",
+		},
+		{
+			from: "https://godoc.org/-/go",
+			to:   "https://pkg.go.dev/std?tab=packages&utm_source=godoc",
+		},
+		{
+			from: "https://godoc.org/?q=foo",
+			to:   "https://pkg.go.dev/search?q=foo&utm_source=godoc",
+		},
+		{
+			from: "https://godoc.org/cloud.google.com/go/storage",
+			to:   "https://pkg.go.dev/cloud.google.com/go/storage?tab=doc&utm_source=godoc",
+		},
+		{
+			from: "https://godoc.org/cloud.google.com/go/storage?imports",
+			to:   "https://pkg.go.dev/cloud.google.com/go/storage?tab=imports&utm_source=godoc",
+		},
+		{
+			from: "https://godoc.org/cloud.google.com/go/storage?importers",
+			to:   "https://pkg.go.dev/cloud.google.com/go/storage?tab=importedby&utm_source=godoc",
+		},
+	}
+
+	for _, tc := range testCases {
+		u, err := url.Parse(tc.from)
+		if err != nil {
+			t.Errorf("url.Parse(%q): %v", tc.from, err)
+			continue
+		}
+		to := pkgGoDevURL(u)
+		if got, want := to.String(), tc.to; got != want {
+			t.Errorf("pkgGoDevURL(%q) = %q; want %q", u, got, want)
+		}
+	}
+}
+
 func TestNewGDDOEvent(t *testing.T) {
 	for _, test := range []struct {
 		url  string