cmd/googlegolangorg: add GAE app that serves google.golang.org

This code was originally in Google's internal repo, but there's
no good reason for it to stay there.

Change-Id: Id2cd2b2bb3691a14182a02e01c40005652106402
Reviewed-on: https://go-review.googlesource.com/c/website/+/191160
Reviewed-by: Katie Hockman <katie@golang.org>
diff --git a/cmd/googlegolangorg/.gcloudignore b/cmd/googlegolangorg/.gcloudignore
new file mode 100644
index 0000000..199e6d9
--- /dev/null
+++ b/cmd/googlegolangorg/.gcloudignore
@@ -0,0 +1,25 @@
+# This file specifies files that are *not* uploaded to Google Cloud Platform
+# using gcloud. It follows the same syntax as .gitignore, with the addition of
+# "#!include" directives (which insert the entries of the given .gitignore-style
+# file at that point).
+#
+# For more information, run:
+#   $ gcloud topic gcloudignore
+#
+.gcloudignore
+# If you would like to upload your .git directory, .gitignore file or files
+# from your .gitignore file, remove the corresponding line
+# below:
+.git
+.gitignore
+
+# Binaries for programs and plugins
+*.exe
+*.exe~
+*.dll
+*.so
+*.dylib
+# Test binary, build with `go test -c`
+*.test
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
\ No newline at end of file
diff --git a/cmd/googlegolangorg/README.md b/cmd/googlegolangorg/README.md
new file mode 100644
index 0000000..4e1aeac
--- /dev/null
+++ b/cmd/googlegolangorg/README.md
@@ -0,0 +1,18 @@
+This trivial App Engine app serves the small meta+redirector HTML pages
+for https://google.golang.org/. For example:
+
+- https://google.golang.org/appengine
+- https://google.golang.org/cloud
+- https://google.golang.org/api/
+- https://google.golang.org/api/storage/v1
+- https://google.golang.org/grpc
+
+The page includes a meta tag to instruct the go tool to translate e.g. the
+import path "google.golang.org/appengine" to "github.com/golang/appengine".
+See `go help importpath` for the mechanics.
+
+To update the public site, run:
+
+```
+gcloud app --account=username@domain.com --project=golang-org deploy --no-promote -v google app.yaml
+```
diff --git a/cmd/googlegolangorg/app.yaml b/cmd/googlegolangorg/app.yaml
new file mode 100644
index 0000000..ac11081
--- /dev/null
+++ b/cmd/googlegolangorg/app.yaml
@@ -0,0 +1 @@
+runtime: go112
diff --git a/cmd/googlegolangorg/main.go b/cmd/googlegolangorg/main.go
new file mode 100644
index 0000000..efddcf9
--- /dev/null
+++ b/cmd/googlegolangorg/main.go
@@ -0,0 +1,139 @@
+// A trivial redirector for google.golang.org.
+package main
+
+import (
+	"fmt"
+	"html/template"
+	"net/http"
+	"os"
+	"strings"
+)
+
+var repoMap = map[string]*repoImport{
+	"api": {
+		VCS: "git",
+		URL: "https://code.googlesource.com/google-api-go-client",
+		Src: github("google/google-api-go-client"),
+	},
+	"appengine": {
+		VCS: "git",
+		URL: "https://github.com/golang/appengine",
+		Src: github("golang/appengine"),
+	},
+	"cloud": {
+		// This repo is now at "cloud.google.com/go", but still specifying the repo
+		// here gives nicer errors in godoc.org and the go tool.
+		VCS: "git",
+		URL: "https://code.googlesource.com/gocloud",
+		Src: github("GoogleCloudPlatform/gcloud-golang"),
+	},
+	"genproto": {
+		VCS: "git",
+		URL: "https://github.com/google/go-genproto",
+		Src: github("google/go-genproto"),
+	},
+	"grpc": {
+		VCS: "git",
+		URL: "https://github.com/grpc/grpc-go",
+		Src: github("grpc/grpc-go"),
+	},
+	"protobuf": {
+		VCS: "git",
+		URL: "https://go.googlesource.com/protobuf",
+		Src: github("protocolbuffers/protobuf-go"),
+	},
+}
+
+// repoImport represents an import meta-tag, as per
+// https://golang.org/cmd/go/#hdr-Import_path_syntax
+type repoImport struct {
+	VCS string
+	URL string
+	Src *src
+}
+
+// src represents a godoc.org source redirect.
+// https://github.com/golang/gddo/search?utf8=%E2%9C%93&q=sourceMeta
+type src struct {
+	URL     string
+	DirTpl  string
+	FileTpl string
+}
+
+// github returns the *src representing a github repo.
+func github(base string) *src {
+	return &src{
+		URL:     fmt.Sprintf("https://github.com/%s", base),
+		DirTpl:  fmt.Sprintf("https://github.com/%s/tree/master{/dir}", base),
+		FileTpl: fmt.Sprintf("https://github.com/%s/tree/master{/dir}/{file}#L{line}", base),
+	}
+}
+
+func googsource(repo, base string) *src {
+	return &src{
+		URL:     fmt.Sprintf("https://%s.googlesource.com/%s", repo, base),
+		DirTpl:  fmt.Sprintf("https://%s.googlesource.com/%s/+/master{/dir}", repo, base),
+		FileTpl: fmt.Sprintf("https://%s.googlesource.com/%s/+/master{/dir}/{file}#{line}", repo, base),
+	}
+}
+
+func main() {
+	http.HandleFunc("/", handler)
+
+	port := os.Getenv("PORT")
+	if port == "" {
+		port = "8080"
+		fmt.Printf("Defaulting to port %s\n", port)
+	}
+
+	fmt.Printf("Listening on port %s\n", port)
+	if err := http.ListenAndServe(fmt.Sprintf(":%s", port), nil); err != nil {
+		fmt.Fprintf(os.Stderr, "http.ListenAndServe: %v\n", err)
+		return
+	}
+}
+
+func handler(w http.ResponseWriter, r *http.Request) {
+	head, tail := strings.TrimPrefix(r.URL.Path, "/"), ""
+	if i := strings.Index(head, "/"); i != -1 {
+		head, tail = head[:i], head[i:]
+	}
+	if head == "" {
+		http.Redirect(w, r, "https://cloud.google.com/go/google.golang.org", http.StatusFound)
+		return
+	}
+	repo, ok := repoMap[head]
+	if !ok {
+		http.NotFound(w, r)
+		return
+	}
+	godoc := "https://godoc.org/google.golang.org/" + head + tail
+	// For users visting in a browser, redirect straight to godoc.org.
+	if isBrowser := r.FormValue("go-get") == ""; isBrowser {
+		http.Redirect(w, r, godoc, http.StatusFound)
+		return
+	}
+	data := struct {
+		Head, GoDoc string
+		Repo        *repoImport
+	}{head, godoc, repo}
+	w.Header().Set("Content-Type", "text/html; charset=utf-8")
+	if err := tmpl.Execute(w, data); err != nil {
+		fmt.Fprintf(os.Stderr, "tmpl.Execute: %v\n", err)
+	}
+}
+
+var tmpl = template.Must(template.New("redir").Parse(`<!DOCTYPE html>
+<html>
+<head>
+<meta name="go-import" content="google.golang.org/{{.Head}} {{.Repo.VCS}} {{.Repo.URL}}">
+{{if .Repo.Src}}
+<meta name="go-source" content="google.golang.org/{{.Head}} {{.Repo.Src.URL}} {{.Repo.Src.DirTpl}} {{.Repo.Src.FileTpl}}">
+{{end}}
+<meta http-equiv="refresh" content="0; url={{.GoDoc}}">
+</head>
+<body>
+Nothing to see here. Please <a href="{{.GoDoc}}">move along</a>.
+</body>
+</html>
+`))