internal/redirect: support cl patchset redirects

Previously redirects of the format cl/1234/4 would throw a 404, this
change allows this.

Fixes golang/go#52023

Change-Id: Iedbdf340164b2a1e11c1c4137c93655b50070f91
Reviewed-on: https://go-review.googlesource.com/c/website/+/396494
Reviewed-by: Russ Cox <rsc@golang.org>
diff --git a/internal/redirect/redirect.go b/internal/redirect/redirect.go
index 0c1d227..1078f29 100644
--- a/internal/redirect/redirect.go
+++ b/internal/redirect/redirect.go
@@ -159,7 +159,8 @@
 	})
 }
 
-var validID = regexp.MustCompile(`^[A-Za-z0-9\-._]*/?$`)
+// validPrefixID is used to validate issue and wiki path suffixes
+var validPrefixID = regexp.MustCompile(`^[A-Za-z0-9\-._]*/?$`)
 
 func PrefixHandler(prefix, baseURL string) http.Handler {
 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -169,7 +170,7 @@
 			return
 		}
 		id := r.URL.Path[len(prefix):]
-		if !validID.MatchString(id) {
+		if !validPrefixID.MatchString(id) {
 			http.Error(w, "Not found", http.StatusNotFound)
 			return
 		}
@@ -211,6 +212,10 @@
 	http.Redirect(w, r, r.URL.String(), http.StatusMovedPermanently)
 }
 
+// validCLID is used to validate cl path suffxies. It supports both the
+// bare ID, as well as the patchset syntax (i.e. 1234/2.)
+var validCLID = regexp.MustCompile(`^[0-9]+(/[0-9]+)?/?$`)
+
 func clHandler(w http.ResponseWriter, r *http.Request) {
 	const prefix = "/cl/"
 	if p := r.URL.Path; p == prefix {
@@ -221,10 +226,18 @@
 	id := r.URL.Path[len(prefix):]
 	// support /cl/152700045/, which is used in commit 0edafefc36.
 	id = strings.TrimSuffix(id, "/")
-	if !validID.MatchString(id) {
+	if !validCLID.MatchString(id) {
 		http.Error(w, "Not found", http.StatusNotFound)
 		return
 	}
+
+	// If the ID contains a slash, it is likely pointing towards a
+	// specific patchset. In that case, prefix the id with 'c/',
+	// which Gerrit uses to indicate a specific revision.
+	if strings.Contains(id, "/") {
+		id = "c/" + id
+	}
+
 	target := ""
 
 	if n, err := strconv.Atoi(id); err == nil && isRietveldCL(n) {
diff --git a/internal/redirect/redirect_test.go b/internal/redirect/redirect_test.go
index 0ffdf23..1df1387 100644
--- a/internal/redirect/redirect_test.go
+++ b/internal/redirect/redirect_test.go
@@ -71,6 +71,9 @@
 		"/cl/267120043":  {302, "https://codereview.appspot.com/267120043"},
 		"/cl/267120043/": {302, "https://codereview.appspot.com/267120043"},
 
+		"/cl/1/3":   {302, "https://go-review.googlesource.com/c/1/3"},
+		"/cl/c/1/3": errorResult(404),
+
 		// Verify that we're using the Rietveld CL table:
 		"/cl/152046": {302, "https://codereview.appspot.com/152046"},
 		"/cl/152047": {302, "https://go-review.googlesource.com/152047"},