cmd/golangorg: clean up, centralize handler registration

All handlers (except the App Engine production ones) should
be registered on the mux protected by hostEnforcerHandler.
Do that, and sort the registrations for easy scanning (by people).

Change-Id: I96813eb105f63bf37abed4898ccf23d1f16cc8d4
Reviewed-on: https://go-review.googlesource.com/c/website/+/293416
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
diff --git a/cmd/golangorg/blog.go b/cmd/golangorg/blog.go
deleted file mode 100644
index e9edade..0000000
--- a/cmd/golangorg/blog.go
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-//go:build go1.16
-// +build go1.16
-
-package main
-
-import (
-	"fmt"
-	"go/build"
-	"log"
-	"net/http"
-	"os"
-	"path/filepath"
-	"runtime"
-	"strings"
-	"sync"
-
-	"golang.org/x/tools/blog"
-	"golang.org/x/website/internal/redirect"
-)
-
-const (
-	blogRepo = "golang.org/x/blog"
-	blogURL  = "https://blog.golang.org/"
-	blogPath = "/blog/"
-)
-
-var (
-	blogServer   http.Handler // set by blogInit
-	blogInitOnce sync.Once
-	playEnabled  bool
-)
-
-func init() {
-	// Initialize blog only when first accessed.
-	http.HandleFunc(blogPath, func(w http.ResponseWriter, r *http.Request) {
-		blogInitOnce.Do(func() {
-			blogInit(r.Host)
-		})
-		blogServer.ServeHTTP(w, r)
-	})
-}
-
-func blogInit(host string) {
-	// Binary distributions included the blog content in "/blog".
-	// We stopped including this in Go 1.11.
-	root := filepath.Join(runtime.GOROOT(), "blog")
-
-	// Prefer content from the golang.org/x/blog repository if present.
-	if pkg, err := build.Import(blogRepo, "", build.FindOnly); err == nil {
-		root = pkg.Dir
-	}
-
-	// If content is not available fall back to redirect.
-	if fi, err := os.Stat(root); err != nil || !fi.IsDir() {
-		fmt.Fprintf(os.Stderr, "Blog content not available locally. "+
-			"To install, run \n\tgo get %v\n", blogRepo)
-		blogServer = http.HandlerFunc(blogRedirectHandler)
-		return
-	}
-
-	s, err := blog.NewServer(blog.Config{
-		BaseURL:         blogPath,
-		BasePath:        strings.TrimSuffix(blogPath, "/"),
-		ContentPath:     filepath.Join(root, "content"),
-		TemplatePath:    filepath.Join(root, "template"),
-		HomeArticles:    5,
-		PlayEnabled:     playEnabled,
-		ServeLocalLinks: strings.HasPrefix(host, "localhost"),
-	})
-	if err != nil {
-		log.Fatal(err)
-	}
-	blogServer = s
-}
-
-func blogRedirectHandler(w http.ResponseWriter, r *http.Request) {
-	if r.URL.Path == blogPath {
-		http.Redirect(w, r, blogURL, http.StatusFound)
-		return
-	}
-	blogPrefixHandler.ServeHTTP(w, r)
-}
-
-var blogPrefixHandler = redirect.PrefixHandler(blogPath, blogURL)
diff --git a/cmd/golangorg/handlers.go b/cmd/golangorg/handlers.go
index 57c6cc5..5412c2f 100644
--- a/cmd/golangorg/handlers.go
+++ b/cmd/golangorg/handlers.go
@@ -75,14 +75,16 @@
 		panic("nil Presentation")
 	}
 	mux := http.NewServeMux()
-	mux.HandleFunc("/doc/codewalk/", codewalk)
-	mux.Handle("/doc/play/", pres.FileServer())
-	mux.Handle("/robots.txt", pres.FileServer())
 	mux.Handle("/", pres)
-	mux.Handle("/pkg/C/", redirect.Handler("/cmd/cgo/"))
-	mux.HandleFunc("/fmt", fmtHandler)
+	mux.Handle("/blog/", http.HandlerFunc(blogHandler))
+	mux.Handle("/doc/codewalk/", http.HandlerFunc(codewalk))
 	mux.Handle("/doc/devel/release.html", releaseHandler{ReleaseHistory: sortReleases(history.Releases)})
-	handleRootAndSubtree(mux, "/project/", projectHandler{ReleaseHistory: sortMajorReleases(history.Releases)}, pres)
+	mux.Handle("/doc/play/", pres.FileServer())
+	mux.Handle("/fmt", http.HandlerFunc(fmtHandler))
+	mux.Handle("/pkg/C/", redirect.Handler("/cmd/cgo/"))
+	mux.Handle("/project/", projectHandler{ReleaseHistory: sortMajorReleases(history.Releases)})
+	mux.Handle("/robots.txt", pres.FileServer())
+	mux.Handle("/x/", http.HandlerFunc(xHandler))
 	redirect.Register(mux)
 
 	http.Handle("/", hostEnforcerHandler{mux})
@@ -90,26 +92,6 @@
 	return mux
 }
 
-// handleRootAndSubtree registers a handler for the given pattern in mux.
-// The handler selects between root or subtree handlers to handle requests.
-//
-// The root handler is used for requests with URL path equal to the pattern,
-// and the subtree handler is used for all other requests matched by pattern.
-//
-// The pattern must have a trailing slash ('/'), otherwise handleRoot panics.
-func handleRootAndSubtree(mux *http.ServeMux, path string, root, subtree http.Handler) {
-	if !strings.HasSuffix(path, "/") {
-		panic("handleRootAndSubtree must be used on patterns with a trailing slash ('/')")
-	}
-	mux.HandleFunc(path, func(w http.ResponseWriter, req *http.Request) {
-		if req.URL.Path == path {
-			root.ServeHTTP(w, req)
-		} else {
-			subtree.ServeHTTP(w, req)
-		}
-	})
-}
-
 func readTemplate(name string) *template.Template {
 	if pres == nil {
 		panic("no global Presentation set yet")
@@ -164,3 +146,7 @@
 	w.Header().Set("Content-type", "application/json; charset=utf-8")
 	json.NewEncoder(w).Encode(resp)
 }
+
+func blogHandler(w http.ResponseWriter, r *http.Request) {
+	http.Redirect(w, r, "https://blog.golang.org"+strings.TrimPrefix(r.URL.Path, "/blog"), http.StatusFound)
+}
diff --git a/cmd/golangorg/local.go b/cmd/golangorg/local.go
index 2d9c794..37353b3 100644
--- a/cmd/golangorg/local.go
+++ b/cmd/golangorg/local.go
@@ -36,5 +36,5 @@
 
 func lateSetup(mux *http.ServeMux) {
 	// Register a redirect handler for /dl/ to the golang.org download page.
-	http.Handle("/dl/", http.RedirectHandler("https://golang.org/dl/", http.StatusFound))
+	mux.Handle("/dl/", http.RedirectHandler("https://golang.org/dl/", http.StatusFound))
 }
diff --git a/cmd/golangorg/main.go b/cmd/golangorg/main.go
index 83f71c6..abd6f0a 100644
--- a/cmd/golangorg/main.go
+++ b/cmd/golangorg/main.go
@@ -65,8 +65,6 @@
 	flag.Usage = usage
 	flag.Parse()
 
-	playEnabled = *showPlayground
-
 	// Check usage.
 	if flag.NArg() > 0 {
 		fmt.Fprintln(os.Stderr, "Unexpected arguments.")
diff --git a/cmd/golangorg/project.go b/cmd/golangorg/project.go
index ca57382..f8a05bf 100644
--- a/cmd/golangorg/project.go
+++ b/cmd/golangorg/project.go
@@ -20,12 +20,17 @@
 	"golang.org/x/website/internal/history"
 )
 
-// projectHandler serves The Go Project page.
+// projectHandler serves The Go Project page on /project/.
 type projectHandler struct {
 	ReleaseHistory []MajorRelease // Pre-computed release history to display.
 }
 
 func (h projectHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+	if req.URL.Path != "/project/" {
+		pres.ServeHTTP(w, req) // 404
+		return
+	}
+
 	const relPath = "doc/contrib.html"
 
 	src, err := vfs.ReadFile(fs, relPath)
diff --git a/cmd/golangorg/x.go b/cmd/golangorg/x.go
index e65e03c..a3c8412 100644
--- a/cmd/golangorg/x.go
+++ b/cmd/golangorg/x.go
@@ -20,19 +20,13 @@
 	"golang.org/x/build/repos"
 )
 
-const xPrefix = "/x/"
-
-func init() {
-	http.HandleFunc(xPrefix, xHandler)
-}
-
 func xHandler(w http.ResponseWriter, r *http.Request) {
-	if !strings.HasPrefix(r.URL.Path, xPrefix) {
+	if !strings.HasPrefix(r.URL.Path, "/x/") {
 		// Shouldn't happen if handler is registered correctly.
 		http.Redirect(w, r, "https://pkg.go.dev/search?q=golang.org/x", http.StatusTemporaryRedirect)
 		return
 	}
-	proj, suffix := strings.TrimPrefix(r.URL.Path, xPrefix), ""
+	proj, suffix := strings.TrimPrefix(r.URL.Path, "/x/"), ""
 	if i := strings.Index(proj, "/"); i != -1 {
 		proj, suffix = proj[:i], proj[i:]
 	}