cmd/golangorg: merge into single source file

There is so little left here that the file
boundaries make no sense anymore.
Move all the functions into main.go.
registerHandlers was inlined instead of
copied.

Change-Id: If91a5d1dc75834747b9d803ec44790950d503aed
Reviewed-on: https://go-review.googlesource.com/c/website/+/328011
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
diff --git a/cmd/golangorg/godoc.go b/cmd/golangorg/godoc.go
deleted file mode 100644
index 8ff0960..0000000
--- a/cmd/golangorg/godoc.go
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2020 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.
-
-package main
-
-import (
-	"net/http"
-	"strings"
-
-	"golang.org/x/website/internal/env"
-)
-
-// googleCN reports whether request r is considered
-// to be served from golang.google.cn.
-// TODO: This is duplicated within internal/proxy. Move to a common location.
-func googleCN(r *http.Request) bool {
-	if r.FormValue("googlecn") != "" {
-		return true
-	}
-	if strings.HasSuffix(r.Host, ".cn") {
-		return true
-	}
-	if !env.CheckCountry() {
-		return false
-	}
-	switch r.Header.Get("X-Appengine-Country") {
-	case "", "ZZ", "CN":
-		return true
-	}
-	return false
-}
diff --git a/cmd/golangorg/handlers.go b/cmd/golangorg/handlers.go
deleted file mode 100644
index 694e1fe..0000000
--- a/cmd/golangorg/handlers.go
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright 2010 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.
-
-package main
-
-import (
-	"encoding/json"
-	"go/format"
-	"net/http"
-	"strings"
-
-	"golang.org/x/website/internal/backport/io/fs"
-	"golang.org/x/website/internal/codewalk"
-	"golang.org/x/website/internal/env"
-	"golang.org/x/website/internal/redirect"
-	"golang.org/x/website/internal/web"
-)
-
-// hostEnforcerHandler redirects http://foo.golang.org/bar to https://golang.org/bar.
-// It permits golang.google.cn for China and *-dot-golang-org.appspot.com for testing.
-type hostEnforcerHandler struct {
-	h http.Handler
-}
-
-func (h hostEnforcerHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
-	if !env.EnforceHosts() {
-		h.h.ServeHTTP(w, r)
-		return
-	}
-	if !h.isHTTPS(r) || !h.validHost(r.Host) {
-		r.URL.Scheme = "https"
-		if h.validHost(r.Host) {
-			r.URL.Host = r.Host
-		} else {
-			r.URL.Host = "golang.org"
-		}
-		http.Redirect(w, r, r.URL.String(), http.StatusFound)
-		return
-	}
-	w.Header().Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload")
-	h.h.ServeHTTP(w, r)
-}
-
-func (h hostEnforcerHandler) isHTTPS(r *http.Request) bool {
-	return r.TLS != nil || r.Header.Get("X-Forwarded-Proto") == "https"
-}
-
-func (h hostEnforcerHandler) validHost(host string) bool {
-	switch strings.ToLower(host) {
-	case "golang.org", "golang.google.cn":
-		return true
-	}
-	if strings.HasSuffix(host, "-dot-golang-org.appspot.com") {
-		// staging/test
-		return true
-	}
-	return false
-}
-
-func registerHandlers(fsys fs.FS, site *web.Site) *http.ServeMux {
-	if site == nil {
-		panic("nil Site")
-	}
-	mux := http.NewServeMux()
-	mux.Handle("/", site)
-	mux.Handle("/doc/codewalk/", codewalk.NewServer(fsys, site))
-	mux.Handle("/fmt", http.HandlerFunc(fmtHandler))
-	mux.Handle("/x/", http.HandlerFunc(xHandler))
-	redirect.Register(mux)
-
-	http.Handle("/", hostEnforcerHandler{mux})
-
-	return mux
-}
-
-type fmtResponse struct {
-	Body  string
-	Error string
-}
-
-// fmtHandler takes a Go program in its "body" form value, formats it with
-// standard gofmt formatting, and writes a fmtResponse as a JSON object.
-func fmtHandler(w http.ResponseWriter, r *http.Request) {
-	resp := new(fmtResponse)
-	body, err := format.Source([]byte(r.FormValue("body")))
-	if err != nil {
-		resp.Error = err.Error()
-	} else {
-		resp.Body = string(body)
-	}
-	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/regtest_test.go b/cmd/golangorg/regtest_test.go
deleted file mode 100644
index dc0416b..0000000
--- a/cmd/golangorg/regtest_test.go
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2018 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.
-
-// Regression tests to run against a production instance of golangorg.
-
-package main_test
-
-import (
-	"flag"
-	"strings"
-	"testing"
-
-	"golang.org/x/website/internal/webtest"
-)
-
-var host = flag.String("regtest.host", "", "host to run regression test against")
-
-func TestLiveServer(t *testing.T) {
-	*host = strings.TrimSuffix(*host, "/")
-	if *host == "" {
-		t.Skip("regtest.host flag missing.")
-	}
-
-	webtest.TestServer(t, "testdata/*.txt", *host)
-}
diff --git a/cmd/golangorg/main.go b/cmd/golangorg/server.go
similarity index 62%
rename from cmd/golangorg/main.go
rename to cmd/golangorg/server.go
index 0d649e3..23e809f 100644
--- a/cmd/golangorg/main.go
+++ b/cmd/golangorg/server.go
@@ -19,8 +19,10 @@
 
 import (
 	"context"
+	"encoding/json"
 	"flag"
 	"fmt"
+	"go/format"
 	"io"
 	"log"
 	"net/http"
@@ -30,11 +32,15 @@
 	"strings"
 
 	"cloud.google.com/go/datastore"
+	"golang.org/x/build/repos"
 	"golang.org/x/website"
 	"golang.org/x/website/internal/backport/archive/zip"
+	"golang.org/x/website/internal/backport/html/template"
 	"golang.org/x/website/internal/backport/io/fs"
 	"golang.org/x/website/internal/backport/osfs"
+	"golang.org/x/website/internal/codewalk"
 	"golang.org/x/website/internal/dl"
+	"golang.org/x/website/internal/env"
 	"golang.org/x/website/internal/memcache"
 	"golang.org/x/website/internal/proxy"
 	"golang.org/x/website/internal/redirect"
@@ -62,13 +68,6 @@
 	os.Exit(2)
 }
 
-func loggingHandler(h http.Handler) http.Handler {
-	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
-		log.Printf("%s\t%s", req.RemoteAddr, req.URL)
-		h.ServeHTTP(w, req)
-	})
-}
-
 func main() {
 	repoRoot := "../.."
 	if _, err := os.Stat("_content"); err == nil {
@@ -130,7 +129,13 @@
 	}
 	site.GoogleCN = googleCN
 
-	mux := registerHandlers(fsys, site)
+	mux := http.NewServeMux()
+	mux.Handle("/", site)
+	mux.Handle("/doc/codewalk/", codewalk.NewServer(fsys, site))
+	mux.Handle("/fmt", http.HandlerFunc(fmtHandler))
+	mux.Handle("/x/", http.HandlerFunc(xHandler))
+	redirect.Register(mux)
+	http.Handle("/", hostEnforcerHandler{mux})
 
 	http.HandleFunc("/robots.txt", func(w http.ResponseWriter, r *http.Request) {
 		io.WriteString(w, "User-agent: *\nDisallow: /search\n")
@@ -163,6 +168,169 @@
 	}
 }
 
+func appEngineSetup(site *web.Site, mux *http.ServeMux) {
+	site.GoogleAnalytics = os.Getenv("GOLANGORG_ANALYTICS")
+
+	ctx := context.Background()
+
+	datastoreClient, err := datastore.NewClient(ctx, "")
+	if err != nil {
+		if strings.Contains(err.Error(), "missing project") {
+			log.Fatalf("Missing datastore project. Set the DATASTORE_PROJECT_ID env variable. Use `gcloud beta emulators datastore` to start a local datastore.")
+		}
+		log.Fatalf("datastore.NewClient: %v.", err)
+	}
+
+	redisAddr := os.Getenv("GOLANGORG_REDIS_ADDR")
+	if redisAddr == "" {
+		log.Fatalf("Missing redis server for golangorg in production mode. set GOLANGORG_REDIS_ADDR environment variable.")
+	}
+	memcacheClient := memcache.New(redisAddr)
+
+	dl.RegisterHandlers(mux, site, datastoreClient, memcacheClient)
+	short.RegisterHandlers(mux, datastoreClient, memcacheClient)
+
+	// Register /compile and /share handlers against the default serve mux
+	// so that other app modules can make plain HTTP requests to those
+	// hosts. (For reasons, HTTPS communication between modules is broken.)
+	proxy.RegisterHandlers(http.DefaultServeMux)
+
+	log.Println("AppEngine initialization complete")
+}
+
+// googleCN reports whether request r is considered
+// to be served from golang.google.cn.
+// TODO: This is duplicated within internal/proxy. Move to a common location.
+func googleCN(r *http.Request) bool {
+	if r.FormValue("googlecn") != "" {
+		return true
+	}
+	if strings.HasSuffix(r.Host, ".cn") {
+		return true
+	}
+	if !env.CheckCountry() {
+		return false
+	}
+	switch r.Header.Get("X-Appengine-Country") {
+	case "", "ZZ", "CN":
+		return true
+	}
+	return false
+}
+
+func blogHandler(w http.ResponseWriter, r *http.Request) {
+	http.Redirect(w, r, "https://blog.golang.org"+strings.TrimPrefix(r.URL.Path, "/blog"), http.StatusFound)
+}
+
+type fmtResponse struct {
+	Body  string
+	Error string
+}
+
+// fmtHandler takes a Go program in its "body" form value, formats it with
+// standard gofmt formatting, and writes a fmtResponse as a JSON object.
+func fmtHandler(w http.ResponseWriter, r *http.Request) {
+	resp := new(fmtResponse)
+	body, err := format.Source([]byte(r.FormValue("body")))
+	if err != nil {
+		resp.Error = err.Error()
+	} else {
+		resp.Body = string(body)
+	}
+	w.Header().Set("Content-type", "application/json; charset=utf-8")
+	json.NewEncoder(w).Encode(resp)
+}
+
+// hostEnforcerHandler redirects http://foo.golang.org/bar to https://golang.org/bar.
+// It permits golang.google.cn for China and *-dot-golang-org.appspot.com for testing.
+type hostEnforcerHandler struct {
+	h http.Handler
+}
+
+func (h hostEnforcerHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	if !env.EnforceHosts() {
+		h.h.ServeHTTP(w, r)
+		return
+	}
+	if !h.isHTTPS(r) || !h.validHost(r.Host) {
+		r.URL.Scheme = "https"
+		if h.validHost(r.Host) {
+			r.URL.Host = r.Host
+		} else {
+			r.URL.Host = "golang.org"
+		}
+		http.Redirect(w, r, r.URL.String(), http.StatusFound)
+		return
+	}
+	w.Header().Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload")
+	h.h.ServeHTTP(w, r)
+}
+
+func (h hostEnforcerHandler) isHTTPS(r *http.Request) bool {
+	return r.TLS != nil || r.Header.Get("X-Forwarded-Proto") == "https"
+}
+
+func (h hostEnforcerHandler) validHost(host string) bool {
+	switch strings.ToLower(host) {
+	case "golang.org", "golang.google.cn":
+		return true
+	}
+	if strings.HasSuffix(host, "-dot-golang-org.appspot.com") {
+		// staging/test
+		return true
+	}
+	return false
+}
+
+func loggingHandler(h http.Handler) http.Handler {
+	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
+		log.Printf("%s\t%s", req.RemoteAddr, req.URL)
+		h.ServeHTTP(w, req)
+	})
+}
+
+func xHandler(w http.ResponseWriter, r *http.Request) {
+	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, "/x/"), ""
+	if i := strings.Index(proj, "/"); i != -1 {
+		proj, suffix = proj[:i], proj[i:]
+	}
+	if proj == "" {
+		http.Redirect(w, r, "https://pkg.go.dev/search?q=golang.org/x", http.StatusTemporaryRedirect)
+		return
+	}
+	repo, ok := repos.ByGerritProject[proj]
+	if !ok || !strings.HasPrefix(repo.ImportPath, "golang.org/x/") {
+		http.NotFound(w, r)
+		return
+	}
+	data := struct {
+		Proj   string // Gerrit project ("net", "sys", etc)
+		Suffix string // optional "/path" for requests like /x/PROJ/path
+	}{proj, suffix}
+	if err := xTemplate.Execute(w, data); err != nil {
+		log.Println("xHandler:", err)
+	}
+}
+
+var xTemplate = template.Must(template.New("x").Parse(`<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+<meta name="go-import" content="golang.org/x/{{.Proj}} git https://go.googlesource.com/{{.Proj}}">
+<meta name="go-source" content="golang.org/x/{{.Proj}} https://github.com/golang/{{.Proj}}/ https://github.com/golang/{{.Proj}}/tree/master{/dir} https://github.com/golang/{{.Proj}}/blob/master{/dir}/{file}#L{line}">
+<meta http-equiv="refresh" content="0; url=https://pkg.go.dev/golang.org/x/{{.Proj}}{{.Suffix}}">
+</head>
+<body>
+<a href="https://pkg.go.dev/golang.org/x/{{.Proj}}{{.Suffix}}">Redirecting to documentation...</a>
+</body>
+</html>
+`))
+
 var _ fs.ReadDirFS = unionFS{}
 
 // A unionFS is an FS presenting the union of the file systems in the slice.
@@ -224,33 +392,3 @@
 	}
 	return nil, errOut
 }
-
-func appEngineSetup(site *web.Site, mux *http.ServeMux) {
-	site.GoogleAnalytics = os.Getenv("GOLANGORG_ANALYTICS")
-
-	ctx := context.Background()
-
-	datastoreClient, err := datastore.NewClient(ctx, "")
-	if err != nil {
-		if strings.Contains(err.Error(), "missing project") {
-			log.Fatalf("Missing datastore project. Set the DATASTORE_PROJECT_ID env variable. Use `gcloud beta emulators datastore` to start a local datastore.")
-		}
-		log.Fatalf("datastore.NewClient: %v.", err)
-	}
-
-	redisAddr := os.Getenv("GOLANGORG_REDIS_ADDR")
-	if redisAddr == "" {
-		log.Fatalf("Missing redis server for golangorg in production mode. set GOLANGORG_REDIS_ADDR environment variable.")
-	}
-	memcacheClient := memcache.New(redisAddr)
-
-	dl.RegisterHandlers(mux, site, datastoreClient, memcacheClient)
-	short.RegisterHandlers(mux, datastoreClient, memcacheClient)
-
-	// Register /compile and /share handlers against the default serve mux
-	// so that other app modules can make plain HTTP requests to those
-	// hosts. (For reasons, HTTPS communication between modules is broken.)
-	proxy.RegisterHandlers(http.DefaultServeMux)
-
-	log.Println("AppEngine initialization complete")
-}
diff --git a/cmd/golangorg/godoc_test.go b/cmd/golangorg/server_test.go
similarity index 85%
rename from cmd/golangorg/godoc_test.go
rename to cmd/golangorg/server_test.go
index 829331a..a606276 100644
--- a/cmd/golangorg/godoc_test.go
+++ b/cmd/golangorg/server_test.go
@@ -6,12 +6,14 @@
 
 import (
 	"bytes"
+	"flag"
 	"fmt"
 	"io/ioutil"
 	"net"
 	"net/http"
 	"os"
 	"os/exec"
+	"strings"
 	"testing"
 	"time"
 
@@ -101,4 +103,18 @@
 
 	webtest.TestServer(t, "testdata/web.txt", addr)
 	webtest.TestServer(t, "testdata/release.txt", addr)
+	webtest.TestServer(t, "testdata/x.txt", addr)
+}
+
+// Regression tests to run against a production instance of golangorg.
+
+var host = flag.String("regtest.host", "", "host to run regression test against")
+
+func TestLiveServer(t *testing.T) {
+	*host = strings.TrimSuffix(*host, "/")
+	if *host == "" {
+		t.Skip("regtest.host flag missing.")
+	}
+
+	webtest.TestServer(t, "testdata/*.txt", *host)
 }
diff --git a/cmd/golangorg/x.go b/cmd/golangorg/x.go
deleted file mode 100644
index 741a2f2..0000000
--- a/cmd/golangorg/x.go
+++ /dev/null
@@ -1,60 +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.
-
-// This file contains the handlers that serve go-import redirects for Go
-// sub-repositories. It specifies the mapping from import paths like
-// "golang.org/x/tools" to the actual repository locations.
-
-package main
-
-import (
-	"log"
-	"net/http"
-	"strings"
-
-	"golang.org/x/build/repos"
-	"golang.org/x/website/internal/backport/html/template"
-)
-
-func xHandler(w http.ResponseWriter, r *http.Request) {
-	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, "/x/"), ""
-	if i := strings.Index(proj, "/"); i != -1 {
-		proj, suffix = proj[:i], proj[i:]
-	}
-	if proj == "" {
-		http.Redirect(w, r, "https://pkg.go.dev/search?q=golang.org/x", http.StatusTemporaryRedirect)
-		return
-	}
-	repo, ok := repos.ByGerritProject[proj]
-	if !ok || !strings.HasPrefix(repo.ImportPath, "golang.org/x/") {
-		http.NotFound(w, r)
-		return
-	}
-	data := struct {
-		Proj   string // Gerrit project ("net", "sys", etc)
-		Suffix string // optional "/path" for requests like /x/PROJ/path
-	}{proj, suffix}
-	if err := xTemplate.Execute(w, data); err != nil {
-		log.Println("xHandler:", err)
-	}
-}
-
-var xTemplate = template.Must(template.New("x").Parse(`<!DOCTYPE html>
-<html>
-<head>
-<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
-<meta name="go-import" content="golang.org/x/{{.Proj}} git https://go.googlesource.com/{{.Proj}}">
-<meta name="go-source" content="golang.org/x/{{.Proj}} https://github.com/golang/{{.Proj}}/ https://github.com/golang/{{.Proj}}/tree/master{/dir} https://github.com/golang/{{.Proj}}/blob/master{/dir}/{file}#L{line}">
-<meta http-equiv="refresh" content="0; url=https://pkg.go.dev/golang.org/x/{{.Proj}}{{.Suffix}}">
-</head>
-<body>
-<a href="https://pkg.go.dev/golang.org/x/{{.Proj}}{{.Suffix}}">Redirecting to documentation...</a>
-</body>
-</html>
-`))
diff --git a/cmd/golangorg/x_test.go b/cmd/golangorg/x_test.go
deleted file mode 100644
index 538d8ab..0000000
--- a/cmd/golangorg/x_test.go
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2019 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.
-
-package main
-
-import (
-	"net/http"
-	"testing"
-
-	"golang.org/x/website/internal/webtest"
-)
-
-func TestXHandler(t *testing.T) {
-	webtest.TestHandler(t, "testdata/x.txt", http.HandlerFunc(xHandler))
-}