Skip to content

Commit 2646b7d

Browse files
committed
[release-branch.go1.11] godoc/redirect: display Gerrit/Rietveld CL disambiguation page when needed
For CL numbers that are determined to be Rietveld CLs, instead of immediately redirecting, check whether a Gerrit CL with the same number also exists. Do so by querying the Gerrit API and caching the existing CLs. If both exist, display a very simple disambiguation HTML page. Cache Gerrit CLs that exist, to avoid querying the remote API server more than once per CL. We can't cache Gerrit CLs that don't exist, since they might get created in the future. Fixes golang/go#29645 Change-Id: I08c32dc82a0136788337c5c32028e87428e8d81e Reviewed-on: https://go-review.googlesource.com/c/157237 Run-TryBot: Dmitri Shuralyov <dmitshur@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
1 parent 0b025b1 commit 2646b7d

File tree

1 file changed

+74
-4
lines changed

1 file changed

+74
-4
lines changed

godoc/redirect/redirect.go

Lines changed: 74 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,18 @@
88
package redirect // import "golang.org/x/tools/godoc/redirect"
99

1010
import (
11+
"context"
1112
"fmt"
13+
"html/template"
1214
"net/http"
1315
"os"
1416
"regexp"
1517
"strconv"
1618
"strings"
19+
"sync"
20+
"time"
21+
22+
"golang.org/x/net/context/ctxhttp"
1723
)
1824

1925
// Register registers HTTP handlers that redirect old godoc paths to their new
@@ -193,18 +199,82 @@ func clHandler(w http.ResponseWriter, r *http.Request) {
193199
target := ""
194200

195201
if n, err := strconv.Atoi(id); err == nil && isRietveldCL(n) {
196-
// TODO: Issue 28836: if this Rietveld CL happens to
202+
// Issue 28836: if this Rietveld CL happens to
197203
// also be a Gerrit CL, render a disambiguation HTML
198-
// page with two links instead. We'll need to make an
199-
// RPC (to maintner?) to figure that out. For now just
200-
// redirect to rietveld.
204+
// page with two links instead. We need to make a
205+
// Gerrit API call to figure that out, but we cache
206+
// known Gerrit CLs so it's done at most once per CL.
207+
if ok, err := isGerritCL(r.Context(), n); err == nil && ok {
208+
w.Header().Set("Content-Type", "text/html; charset=utf-8")
209+
clDisambiguationHTML.Execute(w, n)
210+
return
211+
}
212+
201213
target = "https://codereview.appspot.com/" + id
202214
} else {
203215
target = "https://go-review.googlesource.com/" + id
204216
}
205217
http.Redirect(w, r, target, http.StatusFound)
206218
}
207219

220+
var clDisambiguationHTML = template.Must(template.New("").Parse(`<!DOCTYPE html>
221+
<html lang="en">
222+
<head>
223+
<title>Go CL {{.}} Disambiguation</title>
224+
<meta name="viewport" content="width=device-width">
225+
</head>
226+
<body>
227+
CL number {{.}} exists in both Gerrit (the current code review system)
228+
and Rietveld (the previous code review system). Please make a choice:
229+
230+
<ul>
231+
<li><a href="https://go-review.googlesource.com/{{.}}">Gerrit CL {{.}}</a></li>
232+
<li><a href="https://codereview.appspot.com/{{.}}">Rietveld CL {{.}}</a></li>
233+
</ul>
234+
</body>
235+
</html>`))
236+
237+
// isGerritCL reports whether a Gerrit CL with the specified numeric change ID (e.g., "4247")
238+
// is known to exist by querying the Gerrit API at https://go-review.googlesource.com.
239+
// isGerritCL uses gerritCLCache as a cache of Gerrit CL IDs that exist.
240+
func isGerritCL(ctx context.Context, id int) (bool, error) {
241+
// Check cache first.
242+
gerritCLCache.Lock()
243+
ok := gerritCLCache.exist[id]
244+
gerritCLCache.Unlock()
245+
if ok {
246+
return true, nil
247+
}
248+
249+
// Query the Gerrit API Get Change endpoint, as documented at
250+
// https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#get-change.
251+
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
252+
defer cancel()
253+
resp, err := ctxhttp.Get(ctx, nil, fmt.Sprintf("https://go-review.googlesource.com/changes/%d", id))
254+
if err != nil {
255+
return false, err
256+
}
257+
resp.Body.Close()
258+
switch resp.StatusCode {
259+
case http.StatusOK:
260+
// A Gerrit CL with this ID exists. Add it to cache.
261+
gerritCLCache.Lock()
262+
gerritCLCache.exist[id] = true
263+
gerritCLCache.Unlock()
264+
return true, nil
265+
case http.StatusNotFound:
266+
// A Gerrit CL with this ID doesn't exist. It may get created in the future.
267+
return false, nil
268+
default:
269+
return false, fmt.Errorf("unexpected status code: %v", resp.Status)
270+
}
271+
}
272+
273+
var gerritCLCache = struct {
274+
sync.Mutex
275+
exist map[int]bool // exist is a set of Gerrit CL IDs that are known to exist.
276+
}{exist: make(map[int]bool)}
277+
208278
var changeMap *hashMap
209279

210280
// LoadChangeMap loads the specified map of Mercurial to Git revisions,

0 commit comments

Comments
 (0)