cmd/golangorg: remove various special-case handlers

Redirects can be handled by internal/redirect.
Raw-text serving is already handled in internal/godoc.
It can handle robots.txt and /doc/play/ too and avoid
exposing the underlying FileServer.

Change-Id: I11dda651128815b17cbd3a5db6572f21067529a7
Reviewed-on: https://go-review.googlesource.com/c/website/+/317653
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
diff --git a/cmd/golangorg/handlers.go b/cmd/golangorg/handlers.go
index 5dc226c..28a3b80 100644
--- a/cmd/golangorg/handlers.go
+++ b/cmd/golangorg/handlers.go
@@ -83,12 +83,8 @@
 	}
 	mux := http.NewServeMux()
 	mux.Handle("/", pres)
-	mux.Handle("/blog/", http.HandlerFunc(blogHandler))
 	mux.Handle("/doc/codewalk/", http.HandlerFunc(codewalk))
-	mux.Handle("/doc/play/", pres.FileServer())
 	mux.Handle("/fmt", http.HandlerFunc(fmtHandler))
-	mux.Handle("/pkg/C/", redirect.Handler("/cmd/cgo/"))
-	mux.Handle("/robots.txt", pres.FileServer())
 	mux.Handle("/x/", http.HandlerFunc(xHandler))
 	redirect.Register(mux)
 
diff --git a/internal/godoc/pres.go b/internal/godoc/pres.go
index a860023..b60e9b6 100644
--- a/internal/godoc/pres.go
+++ b/internal/godoc/pres.go
@@ -86,10 +86,6 @@
 	return p, nil
 }
 
-func (p *Presentation) FileServer() http.Handler {
-	return p.fileServer
-}
-
 func (p *Presentation) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	p.mux.ServeHTTP(w, r)
 }
diff --git a/internal/godoc/util.go b/internal/godoc/util.go
index e3b42f7..ba062a6 100644
--- a/internal/godoc/util.go
+++ b/internal/godoc/util.go
@@ -10,6 +10,7 @@
 import (
 	"io/fs"
 	"path"
+	"strings"
 	"unicode/utf8"
 )
 
@@ -33,21 +34,18 @@
 	return true
 }
 
-// textExt[x] is true if the extension x indicates a text file, and false otherwise.
-var textExt = map[string]bool{
-	".css": false, // must be served raw
-	".js":  false, // must be served raw
-	".svg": false, // must be served raw
-}
-
 // isTextFile reports whether the file has a known extension indicating
 // a text file, or if a significant chunk of the specified file looks like
 // correct UTF-8; that is, if it is likely that the file contains human-
 // readable text.
 func isTextFile(fsys fs.FS, filename string) bool {
-	// if the extension is known, use it for decision making
-	if isText, found := textExt[path.Ext(filename)]; found {
-		return isText
+	// Various special cases must be served raw, not converted to nice HTML.
+	if filename == "robots.txt" || strings.HasPrefix(filename, "doc/play/") {
+		return false
+	}
+	switch path.Ext(filename) {
+	case ".css", ".js", ".svg":
+		return false
 	}
 
 	// the extension is not known; read an initial chunk
diff --git a/internal/redirect/redirect.go b/internal/redirect/redirect.go
index bcfd6a0..d2d8262 100644
--- a/internal/redirect/redirect.go
+++ b/internal/redirect/redirect.go
@@ -95,8 +95,8 @@
 }
 
 var redirects = map[string]string{
-	"/blog":       "/blog/",
-	"/build":      "http://build.golang.org",
+	"/blog":       "https://blog.golang.org",
+	"/build":      "https://build.golang.org",
 	"/change":     "https://go.googlesource.com/go",
 	"/cl":         "https://go-review.googlesource.com",
 	"/cmd/godoc/": "https://pkg.go.dev/golang.org/x/tools/cmd/godoc",
@@ -104,7 +104,7 @@
 	"/issue/new":  "https://github.com/golang/go/issues/new",
 	"/issues":     "https://github.com/golang/go/issues",
 	"/issues/new": "https://github.com/golang/go/issues/new",
-	"/play":       "http://play.golang.org",
+	"/play":       "https://play.golang.org",
 	"/design":     "https://go.googlesource.com/proposal/+/master/design",
 
 	// In Go 1.2 the references page is part of /doc/.
@@ -114,11 +114,12 @@
 	// "/ref/": "/doc/#references",
 
 	// Be nice to people who are looking in the wrong place.
+	"/pkg/C/":   "/cmd/cgo/",
 	"/doc/mem":  "/ref/mem",
 	"/doc/spec": "/ref/spec",
 
-	"/talks": "http://talks.golang.org",
-	"/tour":  "http://tour.golang.org",
+	"/talks": "https://talks.golang.org",
+	"/tour":  "https://tour.golang.org",
 	"/wiki":  "https://github.com/golang/go/wiki",
 
 	"/doc/articles/c_go_cgo.html":                    "/blog/c-go-cgo",
@@ -135,15 +136,16 @@
 	"/doc/articles/laws_of_reflection.html":          "/blog/laws-of-reflection",
 	"/doc/articles/slices_usage_and_internals.html":  "/blog/go-slices-usage-and-internals",
 	"/doc/go_for_cpp_programmers.html":               "/wiki/GoForCPPProgrammers",
-	"/doc/go_tutorial.html":                          "http://tour.golang.org/",
+	"/doc/go_tutorial.html":                          "https://tour.golang.org/",
 }
 
 var prefixHelpers = map[string]string{
 	"issue":  "https://github.com/golang/go/issues/",
 	"issues": "https://github.com/golang/go/issues/",
-	"play":   "http://play.golang.org/",
-	"talks":  "http://talks.golang.org/",
+	"play":   "https://play.golang.org/",
+	"talks":  "https://talks.golang.org/",
 	"wiki":   "https://github.com/golang/go/wiki/",
+	"blog":   "https://blog.golang.org/",
 }
 
 func Handler(target string) http.Handler {
@@ -156,7 +158,7 @@
 	})
 }
 
-var validID = regexp.MustCompile(`^[A-Za-z0-9-]*/?$`)
+var validID = regexp.MustCompile(`^[A-Za-z0-9\-._]*/?$`)
 
 func PrefixHandler(prefix, baseURL string) http.Handler {
 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -176,7 +178,7 @@
 }
 
 // Redirect requests from the old "/src/pkg/foo" to the new "/src/foo".
-// See http://golang.org/s/go14nopkg
+// See https://golang.org/s/go14nopkg
 func srcPkgHandler(w http.ResponseWriter, r *http.Request) {
 	r.URL.Path = "/src/" + r.URL.Path[len("/src/pkg/"):]
 	http.Redirect(w, r, r.URL.String(), http.StatusMovedPermanently)
diff --git a/internal/redirect/redirect_test.go b/internal/redirect/redirect_test.go
index 1c9ad64..2f81769 100644
--- a/internal/redirect/redirect_test.go
+++ b/internal/redirect/redirect_test.go
@@ -21,15 +21,21 @@
 
 func TestRedirects(t *testing.T) {
 	var tests = map[string]redirectResult{
-		"/build":    {301, "http://build.golang.org"},
-		"/ref":      {301, "/doc/#references"},
-		"/doc/mem":  {301, "/ref/mem"},
-		"/doc/spec": {301, "/ref/spec"},
-		"/tour":     {301, "http://tour.golang.org"},
-		"/foo":      errorResult(404),
+		"/build":       {301, "https://build.golang.org"},
+		"/ref":         {301, "/doc/#references"},
+		"/doc/mem":     {301, "/ref/mem"},
+		"/doc/spec":    {301, "/ref/spec"},
+		"/tour":        {301, "https://tour.golang.org"},
+		"/foo":         errorResult(404),
+		"/blog":        {301, "https://blog.golang.org"},
+		"/blog/":       {302, "/blog"},
+		"/blog/go1.16": {302, "https://blog.golang.org/go1.16"},
 
 		"/pkg/asn1":           {301, "/pkg/encoding/asn1/"},
 		"/pkg/template/parse": {301, "/pkg/text/template/parse/"},
+		"/pkg/C":              {301, "/pkg/C/"},
+		"/pkg/C/":             {301, "/cmd/cgo/"},
+		"/pkg/C/foo":          {301, "/cmd/cgo/"}, // historical
 
 		"/src/pkg/foo": {301, "/src/foo"},
 
@@ -98,6 +104,7 @@
 
 		if resp.StatusCode != want.status {
 			t.Errorf("(path: %q) got status %d, want %d", path, resp.StatusCode, want.status)
+			continue
 		}
 
 		if want.status != 301 && want.status != 302 {