Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / godoc / redirect / redirect.go
1 // Copyright 2013 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 // Package redirect provides hooks to register HTTP handlers that redirect old
6 // godoc paths to their new equivalents and assist in accessing the issue
7 // tracker, wiki, code review system, etc.
8 package redirect // import "golang.org/x/tools/godoc/redirect"
9
10 import (
11         "context"
12         "fmt"
13         "html/template"
14         "net/http"
15         "os"
16         "regexp"
17         "strconv"
18         "strings"
19         "sync"
20         "time"
21
22         "golang.org/x/net/context/ctxhttp"
23 )
24
25 // Register registers HTTP handlers that redirect old godoc paths to their new
26 // equivalents and assist in accessing the issue tracker, wiki, code review
27 // system, etc. If mux is nil it uses http.DefaultServeMux.
28 func Register(mux *http.ServeMux) {
29         if mux == nil {
30                 mux = http.DefaultServeMux
31         }
32         handlePathRedirects(mux, pkgRedirects, "/pkg/")
33         handlePathRedirects(mux, cmdRedirects, "/cmd/")
34         for prefix, redirect := range prefixHelpers {
35                 p := "/" + prefix + "/"
36                 mux.Handle(p, PrefixHandler(p, redirect))
37         }
38         for path, redirect := range redirects {
39                 mux.Handle(path, Handler(redirect))
40         }
41         // NB: /src/pkg (sans trailing slash) is the index of packages.
42         mux.HandleFunc("/src/pkg/", srcPkgHandler)
43         mux.HandleFunc("/cl/", clHandler)
44         mux.HandleFunc("/change/", changeHandler)
45         mux.HandleFunc("/design/", designHandler)
46 }
47
48 func handlePathRedirects(mux *http.ServeMux, redirects map[string]string, prefix string) {
49         for source, target := range redirects {
50                 h := Handler(prefix + target + "/")
51                 p := prefix + source
52                 mux.Handle(p, h)
53                 mux.Handle(p+"/", h)
54         }
55 }
56
57 // Packages that were renamed between r60 and go1.
58 var pkgRedirects = map[string]string{
59         "asn1":              "encoding/asn1",
60         "big":               "math/big",
61         "cmath":             "math/cmplx",
62         "csv":               "encoding/csv",
63         "exec":              "os/exec",
64         "exp/template/html": "html/template",
65         "gob":               "encoding/gob",
66         "http":              "net/http",
67         "http/cgi":          "net/http/cgi",
68         "http/fcgi":         "net/http/fcgi",
69         "http/httptest":     "net/http/httptest",
70         "http/pprof":        "net/http/pprof",
71         "json":              "encoding/json",
72         "mail":              "net/mail",
73         "rand":              "math/rand",
74         "rpc":               "net/rpc",
75         "rpc/jsonrpc":       "net/rpc/jsonrpc",
76         "scanner":           "text/scanner",
77         "smtp":              "net/smtp",
78         "tabwriter":         "text/tabwriter",
79         "template":          "text/template",
80         "template/parse":    "text/template/parse",
81         "url":               "net/url",
82         "utf16":             "unicode/utf16",
83         "utf8":              "unicode/utf8",
84         "xml":               "encoding/xml",
85 }
86
87 // Commands that were renamed between r60 and go1.
88 var cmdRedirects = map[string]string{
89         "gofix":     "fix",
90         "goinstall": "go",
91         "gopack":    "pack",
92         "gotest":    "go",
93         "govet":     "vet",
94         "goyacc":    "yacc",
95 }
96
97 var redirects = map[string]string{
98         "/blog":       "/blog/",
99         "/build":      "http://build.golang.org",
100         "/change":     "https://go.googlesource.com/go",
101         "/cl":         "https://go-review.googlesource.com",
102         "/cmd/godoc/": "http://godoc.org/golang.org/x/tools/cmd/godoc/",
103         "/issue":      "https://github.com/golang/go/issues",
104         "/issue/new":  "https://github.com/golang/go/issues/new",
105         "/issues":     "https://github.com/golang/go/issues",
106         "/issues/new": "https://github.com/golang/go/issues/new",
107         "/play":       "http://play.golang.org",
108         "/design":     "https://go.googlesource.com/proposal/+/master/design",
109
110         // In Go 1.2 the references page is part of /doc/.
111         "/ref": "/doc/#references",
112         // This next rule clobbers /ref/spec and /ref/mem.
113         // TODO(adg): figure out what to do here, if anything.
114         // "/ref/": "/doc/#references",
115
116         // Be nice to people who are looking in the wrong place.
117         "/doc/mem":  "/ref/mem",
118         "/doc/spec": "/ref/spec",
119
120         "/talks": "http://talks.golang.org",
121         "/tour":  "http://tour.golang.org",
122         "/wiki":  "https://github.com/golang/go/wiki",
123
124         "/doc/articles/c_go_cgo.html":                    "/blog/c-go-cgo",
125         "/doc/articles/concurrency_patterns.html":        "/blog/go-concurrency-patterns-timing-out-and",
126         "/doc/articles/defer_panic_recover.html":         "/blog/defer-panic-and-recover",
127         "/doc/articles/error_handling.html":              "/blog/error-handling-and-go",
128         "/doc/articles/gobs_of_data.html":                "/blog/gobs-of-data",
129         "/doc/articles/godoc_documenting_go_code.html":   "/blog/godoc-documenting-go-code",
130         "/doc/articles/gos_declaration_syntax.html":      "/blog/gos-declaration-syntax",
131         "/doc/articles/image_draw.html":                  "/blog/go-imagedraw-package",
132         "/doc/articles/image_package.html":               "/blog/go-image-package",
133         "/doc/articles/json_and_go.html":                 "/blog/json-and-go",
134         "/doc/articles/json_rpc_tale_of_interfaces.html": "/blog/json-rpc-tale-of-interfaces",
135         "/doc/articles/laws_of_reflection.html":          "/blog/laws-of-reflection",
136         "/doc/articles/slices_usage_and_internals.html":  "/blog/go-slices-usage-and-internals",
137         "/doc/go_for_cpp_programmers.html":               "/wiki/GoForCPPProgrammers",
138         "/doc/go_tutorial.html":                          "http://tour.golang.org/",
139 }
140
141 var prefixHelpers = map[string]string{
142         "issue":  "https://github.com/golang/go/issues/",
143         "issues": "https://github.com/golang/go/issues/",
144         "play":   "http://play.golang.org/",
145         "talks":  "http://talks.golang.org/",
146         "wiki":   "https://github.com/golang/go/wiki/",
147 }
148
149 func Handler(target string) http.Handler {
150         return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
151                 url := target
152                 if qs := r.URL.RawQuery; qs != "" {
153                         url += "?" + qs
154                 }
155                 http.Redirect(w, r, url, http.StatusMovedPermanently)
156         })
157 }
158
159 var validID = regexp.MustCompile(`^[A-Za-z0-9-]*/?$`)
160
161 func PrefixHandler(prefix, baseURL string) http.Handler {
162         return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
163                 if p := r.URL.Path; p == prefix {
164                         // redirect /prefix/ to /prefix
165                         http.Redirect(w, r, p[:len(p)-1], http.StatusFound)
166                         return
167                 }
168                 id := r.URL.Path[len(prefix):]
169                 if !validID.MatchString(id) {
170                         http.Error(w, "Not found", http.StatusNotFound)
171                         return
172                 }
173                 target := baseURL + id
174                 http.Redirect(w, r, target, http.StatusFound)
175         })
176 }
177
178 // Redirect requests from the old "/src/pkg/foo" to the new "/src/foo".
179 // See http://golang.org/s/go14nopkg
180 func srcPkgHandler(w http.ResponseWriter, r *http.Request) {
181         r.URL.Path = "/src/" + r.URL.Path[len("/src/pkg/"):]
182         http.Redirect(w, r, r.URL.String(), http.StatusMovedPermanently)
183 }
184
185 func clHandler(w http.ResponseWriter, r *http.Request) {
186         const prefix = "/cl/"
187         if p := r.URL.Path; p == prefix {
188                 // redirect /prefix/ to /prefix
189                 http.Redirect(w, r, p[:len(p)-1], http.StatusFound)
190                 return
191         }
192         id := r.URL.Path[len(prefix):]
193         // support /cl/152700045/, which is used in commit 0edafefc36.
194         id = strings.TrimSuffix(id, "/")
195         if !validID.MatchString(id) {
196                 http.Error(w, "Not found", http.StatusNotFound)
197                 return
198         }
199         target := ""
200
201         if n, err := strconv.Atoi(id); err == nil && isRietveldCL(n) {
202                 // Issue 28836: if this Rietveld CL happens to
203                 // also be a Gerrit CL, render a disambiguation HTML
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
213                 target = "https://codereview.appspot.com/" + id
214         } else {
215                 target = "https://go-review.googlesource.com/" + id
216         }
217         http.Redirect(w, r, target, http.StatusFound)
218 }
219
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
278 var changeMap *hashMap
279
280 // LoadChangeMap loads the specified map of Mercurial to Git revisions,
281 // which is used by the /change/ handler to intelligently map old hg
282 // revisions to their new git equivalents.
283 // It should be called before calling Register.
284 // The file should remain open as long as the process is running.
285 // See the implementation of this package for details.
286 func LoadChangeMap(filename string) error {
287         f, err := os.Open(filename)
288         if err != nil {
289                 return err
290         }
291         m, err := newHashMap(f)
292         if err != nil {
293                 return err
294         }
295         changeMap = m
296         return nil
297 }
298
299 func changeHandler(w http.ResponseWriter, r *http.Request) {
300         const prefix = "/change/"
301         if p := r.URL.Path; p == prefix {
302                 // redirect /prefix/ to /prefix
303                 http.Redirect(w, r, p[:len(p)-1], http.StatusFound)
304                 return
305         }
306         hash := r.URL.Path[len(prefix):]
307         target := "https://go.googlesource.com/go/+/" + hash
308         if git := changeMap.Lookup(hash); git > 0 {
309                 target = fmt.Sprintf("https://go.googlesource.com/%v/+/%v", git.Repo(), git.Hash())
310         }
311         http.Redirect(w, r, target, http.StatusFound)
312 }
313
314 func designHandler(w http.ResponseWriter, r *http.Request) {
315         const prefix = "/design/"
316         if p := r.URL.Path; p == prefix {
317                 // redirect /prefix/ to /prefix
318                 http.Redirect(w, r, p[:len(p)-1], http.StatusFound)
319                 return
320         }
321         name := r.URL.Path[len(prefix):]
322         target := "https://go.googlesource.com/proposal/+/master/design/" + name + ".md"
323         http.Redirect(w, r, target, http.StatusFound)
324 }