internal/web: redirect golang.org/pkg/... to pkg.go.dev/...
This reduces the number of documentation sites we have to one.
Except in China, where we have to keep serving on the one domain
golang.google.cn - there is no pkg.go.dev in China.
And unless people opt out with ?m=old.
For golang/go#44356.
Change-Id: I2a5b788ac861ce37f356287413468497d184fc09
Reviewed-on: https://go-review.googlesource.com/c/website/+/327849
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
diff --git a/cmd/golangorg/testdata/web.txt b/cmd/golangorg/testdata/web.txt
index 676e9d7..addbf91 100644
--- a/cmd/golangorg/testdata/web.txt
+++ b/cmd/golangorg/testdata/web.txt
@@ -18,6 +18,9 @@
body !contains UA-
GET https://golang.org/cmd/compile/internal/amd64/
+redirect == https://pkg.go.dev/cmd/compile/internal/amd64
+
+GET https://golang.org/cmd/compile/internal/amd64/?m=old
body contains href="/src/cmd/compile/internal/amd64/ssa.go"
GET https://golang.org/conduct
@@ -74,57 +77,94 @@
GET https://golang.org/help
body contains Get help
-GET https://golang.org/pkg/fmt/
-body contains Package fmt implements formatted I/O
-
GET https://golang.org/src/fmt/
body contains scan_test.go
GET https://golang.org/src/fmt/print.go
body contains // Println formats using
+GET https://golang.org/pkg/fmt/
+redirect == https://pkg.go.dev/fmt
+
+GET https://golang.org/pkg/fmt/?m=old
+body contains Package fmt implements formatted I/O
+
+GET https://golang.google.cn/pkg/fmt/
+body contains Package fmt implements formatted I/O
+
GET https://golang.org/pkg
redirect == /pkg/
GET https://golang.org/pkg/
+redirect == https://pkg.go.dev/std
+
+GET https://tip.golang.org/pkg/
+redirect == https://pkg.go.dev/std@master
+
+GET https://golang.org/pkg?m=old
+redirect == /pkg/?m=old
+
+GET https://golang.org/pkg/?m=old
body contains Standard library
body contains Package fmt implements formatted I/O
body !contains internal/syscall
body !contains cmd/gc
-GET https://golang.org/pkg/?m=all
+GET https://golang.org/pkg/?m=old,all
body contains Standard library
body contains Package fmt implements formatted I/O
body contains internal/syscall/?m=all
body !contains cmd/gc
GET https://golang.org/pkg/bufio/
-body contains href="/pkg/io/#Writer
+redirect == https://pkg.go.dev/bufio
+
+GET https://golang.org/pkg/bufio/?GOOS=windows&GOARCH=amd64
+redirect == https://pkg.go.dev/bufio?GOOS=windows&GOARCH=amd64
+
+GET https://tip.golang.org/pkg/bufio/
+redirect == https://pkg.go.dev/bufio@master
+
+GET https://golang.org/pkg/bufio/?m=old
+body contains href="/pkg/io/?m=old#Writer
+body !contains href="/pkg/io/#Writer
GET https://golang.org/pkg/database/sql/
+redirect == https://pkg.go.dev/database/sql
+
+GET https://golang.org/pkg/database/sql/?m=old
body contains The number of connections currently in use; added in Go 1.11
body contains The number of idle connections; added in Go 1.11
-GET https://golang.org/cmd/compile/internal/amd64/
-body contains href="/src/cmd/compile/internal/amd64/ssa.go"
-
GET https://golang.org/pkg/math/bits/
+redirect == https://pkg.go.dev/math/bits
+
+GET https://golang.org/pkg/math/bits/?m=old
body contains Added in Go 1.9
GET https://golang.org/pkg/net/
+redirect == https://pkg.go.dev/net
+
+GET https://golang.org/pkg/net/?m=old
body contains // IPv6 scoped addressing zone; added in Go 1.1
-GET https://golang.org/pkg/net/http/
+GET https://golang.org/pkg/net/http/?m=old
body contains title="Added in Go 1.11"
GET https://golang.org/pkg/net/http/httptrace/
+redirect == https://pkg.go.dev/net/http/httptrace
+
+GET https://golang.org/pkg/net/http/httptrace/?m=old
body ~ Got1xxResponse.*// Go 1\.11
body ~ GotFirstResponseByte func\(\)\s*$
-GET https://golang.org/pkg/os/
+GET https://golang.org/pkg/os/?m=old
body contains func Open
GET https://golang.org/pkg/strings/
+redirect == https://pkg.go.dev/strings
+
+GET https://golang.org/pkg/strings/?m=old
body contains href="/src/strings/strings.go"
GET https://golang.org/project
diff --git a/internal/pkgdoc/doc.go b/internal/pkgdoc/doc.go
index edb5a9f..6dd86e8 100644
--- a/internal/pkgdoc/doc.go
+++ b/internal/pkgdoc/doc.go
@@ -70,6 +70,7 @@
ModeAll Mode = 1 << iota // do not filter exports
ModeFlat // show directory in a flat (non-indented) manner
ModeMethods // show all embedded methods
+ ModeOld // do not redirect to pkg.go.dev
ModeBuiltin // don't associate consts, vars, and factory functions with types (not exposed via ?m= query parameter, used for package builtin, see issue 6645)
)
@@ -79,6 +80,7 @@
"all",
"flat",
"methods",
+ "old",
}
// generate a query string for persisting PageInfoMode between pages.
diff --git a/internal/texthtml/ast.go b/internal/texthtml/ast.go
index 76bd4bb..c7e0560 100644
--- a/internal/texthtml/ast.go
+++ b/internal/texthtml/ast.go
@@ -20,16 +20,17 @@
type goLink struct {
path, name string // package path, identifier name
isVal bool // identifier is defined in a const or var declaration
+ oldDocs bool // link to ?m=old docs
}
func (l *goLink) tags() (start, end string) {
switch {
case l.path != "" && l.name == "":
// package path
- return `<a href="/pkg/` + l.path + `/">`, `</a>`
+ return `<a href="/pkg/` + l.path + `/` + l.docSuffix() + `">`, `</a>`
case l.path != "" && l.name != "":
// qualified identifier
- return `<a href="/pkg/` + l.path + `/#` + l.name + `">`, `</a>`
+ return `<a href="/pkg/` + l.path + `/` + l.docSuffix() + `#` + l.name + `">`, `</a>`
case l.path == "" && l.name != "":
// local identifier
if l.isVal {
@@ -42,6 +43,13 @@
return "", ""
}
+func (l *goLink) docSuffix() string {
+ if l.oldDocs {
+ return "?m=old"
+ }
+ return ""
+}
+
// goLinksFor returns the list of links for the identifiers used
// by node in the same order as they appear in the source.
func goLinksFor(node ast.Node) (links []goLink) {
diff --git a/internal/texthtml/texthtml.go b/internal/texthtml/texthtml.go
index 1175fe1..334085a 100644
--- a/internal/texthtml/texthtml.go
+++ b/internal/texthtml/texthtml.go
@@ -37,6 +37,7 @@
Highlight string // highlight matches for this regexp with <span class="highlight">
Selection Selection // mark selected spans with <span class="selection">
AST ast.Node // link uses to declarations, assuming text is formatting of AST
+ OldDocs bool // emit links to ?m=old docs
}
// Format formats text to HTML according to the configuration cfg.
@@ -55,6 +56,11 @@
if cfg.AST != nil {
idents = tokenSelection(text, token.IDENT)
goLinks = goLinksFor(cfg.AST)
+ if cfg.OldDocs {
+ for i := range goLinks {
+ goLinks[i].oldDocs = true
+ }
+ }
}
formatSelections(&buf, text, goLinks, comments, highlights, cfg.Selection, idents)
diff --git a/internal/web/astfuncs.go b/internal/web/astfuncs.go
index 79d193d..4fbe5f7 100644
--- a/internal/web/astfuncs.go
+++ b/internal/web/astfuncs.go
@@ -36,6 +36,7 @@
buf2.Write(texthtml.Format(buf1.Bytes(), texthtml.Config{
AST: n,
GoComments: true,
+ OldDocs: p.OldDocs,
}))
return template.HTML(buf2.String())
}
@@ -50,6 +51,7 @@
var buf2 bytes.Buffer
buf2.Write(texthtml.Format(buf1.Bytes(), texthtml.Config{
GoComments: true,
+ OldDocs: p.OldDocs,
}))
return sanitize(template.HTML(buf2.String()))
diff --git a/internal/web/pkgdoc.go b/internal/web/pkgdoc.go
index 212526d..21dc1cd 100644
--- a/internal/web/pkgdoc.go
+++ b/internal/web/pkgdoc.go
@@ -7,6 +7,7 @@
import (
"log"
"net/http"
+ "net/url"
"path"
"strings"
@@ -29,6 +30,36 @@
relpath = strings.TrimPrefix(relpath, "/")
mode := pkgdoc.ParseMode(r.FormValue("m"))
+
+ // Redirect to pkg.go.dev.
+ // We provide two overrides for the redirect.
+ // First, the request can set ?m=old to get the old pages.
+ // Second, the request can come from China:
+ // since pkg.go.dev is not available in China, we serve the docs directly.
+ if mode&pkgdoc.ModeOld == 0 && !GoogleCN(r) {
+ if relpath == "" {
+ relpath = "std"
+ }
+ suffix := ""
+ if r.Host == "tip.golang.org" {
+ suffix = "@master"
+ }
+ if goos, goarch := r.FormValue("GOOS"), r.FormValue("GOARCH"); goos != "" || goarch != "" {
+ suffix += "?"
+ if goos != "" {
+ suffix += "GOOS=" + url.QueryEscape(goos)
+ }
+ if goarch != "" {
+ if goos != "" {
+ suffix += "&"
+ }
+ suffix += "GOARCH=" + url.QueryEscape(goarch)
+ }
+ }
+ http.Redirect(w, r, "https://pkg.go.dev/"+relpath+suffix, http.StatusTemporaryRedirect)
+ return
+ }
+
if relpath == "builtin" {
// The fake built-in package contains unexported identifiers,
// but we want to show them. Also, disable type association,
@@ -81,6 +112,7 @@
Subtitle: subtitle,
Template: name,
Data: info,
+ OldDocs: mode&pkgdoc.ModeOld != 0,
})
}
diff --git a/internal/web/site.go b/internal/web/site.go
index 245c99e..96da21c 100644
--- a/internal/web/site.go
+++ b/internal/web/site.go
@@ -117,6 +117,9 @@
Template string // template to apply to data (empty string when Data is raw template.HTML)
Data interface{} // data to be rendered into page frame
+ // Filled in for document rendering
+ OldDocs bool // use ?m=old in doc links
+
// Filled in automatically by ServePage
GoogleCN bool // served on golang.google.cn
GoogleAnalytics string // Google Analytics tag