2013-07-16 22:02:35 -06:00
|
|
|
// Copyright 2011 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.
|
|
|
|
|
2018-09-04 10:55:45 -06:00
|
|
|
// +build golangorg
|
2013-07-16 22:02:35 -06:00
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
// This file replaces main.go when running godoc under app-engine.
|
|
|
|
// See README.godoc-app for details.
|
|
|
|
|
|
|
|
import (
|
|
|
|
"archive/zip"
|
2018-09-04 10:55:45 -06:00
|
|
|
"context"
|
|
|
|
"io"
|
2013-07-16 22:02:35 -06:00
|
|
|
"log"
|
2015-09-22 23:00:56 -06:00
|
|
|
"net/http"
|
2017-06-26 22:05:28 -06:00
|
|
|
"os"
|
2013-07-16 22:02:35 -06:00
|
|
|
"path"
|
2014-12-23 16:02:05 -07:00
|
|
|
"regexp"
|
2017-06-26 22:05:28 -06:00
|
|
|
"runtime"
|
2018-09-04 10:55:45 -06:00
|
|
|
"strings"
|
2013-07-16 22:02:35 -06:00
|
|
|
|
2014-11-09 14:50:40 -07:00
|
|
|
"golang.org/x/tools/godoc"
|
2015-09-22 20:17:19 -06:00
|
|
|
"golang.org/x/tools/godoc/dl"
|
|
|
|
"golang.org/x/tools/godoc/proxy"
|
2018-09-04 10:55:45 -06:00
|
|
|
"golang.org/x/tools/godoc/redirect"
|
2015-09-22 20:17:19 -06:00
|
|
|
"golang.org/x/tools/godoc/short"
|
2014-11-09 14:50:40 -07:00
|
|
|
"golang.org/x/tools/godoc/static"
|
|
|
|
"golang.org/x/tools/godoc/vfs"
|
2017-06-26 22:05:28 -06:00
|
|
|
"golang.org/x/tools/godoc/vfs/gatefs"
|
2014-11-09 14:50:40 -07:00
|
|
|
"golang.org/x/tools/godoc/vfs/mapfs"
|
|
|
|
"golang.org/x/tools/godoc/vfs/zipfs"
|
2018-09-04 10:55:45 -06:00
|
|
|
|
|
|
|
"cloud.google.com/go/datastore"
|
|
|
|
"golang.org/x/tools/internal/memcache"
|
2013-07-29 22:23:23 -06:00
|
|
|
)
|
2013-07-16 22:02:35 -06:00
|
|
|
|
2018-09-04 10:55:45 -06:00
|
|
|
func main() {
|
|
|
|
log.SetFlags(log.Lshortfile | log.LstdFlags)
|
|
|
|
|
2017-06-26 22:05:28 -06:00
|
|
|
var (
|
|
|
|
// .zip filename
|
|
|
|
zipFilename = os.Getenv("GODOC_ZIP")
|
|
|
|
|
|
|
|
// goroot directory in .zip file
|
|
|
|
zipGoroot = os.Getenv("GODOC_ZIP_PREFIX")
|
|
|
|
|
|
|
|
// glob pattern describing search index files
|
|
|
|
// (if empty, the index is built at run-time)
|
|
|
|
indexFilenames = os.Getenv("GODOC_INDEX_GLOB")
|
|
|
|
)
|
|
|
|
|
2013-10-02 22:29:16 -06:00
|
|
|
playEnabled = true
|
|
|
|
|
2013-07-16 22:02:35 -06:00
|
|
|
log.Println("initializing godoc ...")
|
|
|
|
log.Printf(".zip file = %s", zipFilename)
|
|
|
|
log.Printf(".zip GOROOT = %s", zipGoroot)
|
|
|
|
log.Printf("index files = %s", indexFilenames)
|
|
|
|
|
2017-06-26 22:05:28 -06:00
|
|
|
if zipFilename != "" {
|
|
|
|
goroot := path.Join("/", zipGoroot) // fsHttp paths are relative to '/'
|
|
|
|
// read .zip file and set up file systems
|
|
|
|
rc, err := zip.OpenReader(zipFilename)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("%s: %s\n", zipFilename, err)
|
|
|
|
}
|
|
|
|
// rc is never closed (app running forever)
|
|
|
|
fs.Bind("/", zipfs.New(rc, zipFilename), goroot, vfs.BindReplace)
|
|
|
|
} else {
|
|
|
|
rootfs := gatefs.New(vfs.OS(runtime.GOROOT()), make(chan bool, 20))
|
|
|
|
fs.Bind("/", rootfs, "/", vfs.BindReplace)
|
2013-07-16 22:02:35 -06:00
|
|
|
}
|
2017-06-26 22:05:28 -06:00
|
|
|
|
2013-10-02 22:29:16 -06:00
|
|
|
fs.Bind("/lib/godoc", mapfs.New(static.Files), "/", vfs.BindReplace)
|
2013-07-16 22:02:35 -06:00
|
|
|
|
2013-07-29 22:23:23 -06:00
|
|
|
corpus := godoc.NewCorpus(fs)
|
|
|
|
corpus.Verbose = false
|
2014-06-12 23:20:15 -06:00
|
|
|
corpus.MaxResults = 10000 // matches flag default in main.go
|
2013-07-29 22:23:23 -06:00
|
|
|
corpus.IndexEnabled = true
|
|
|
|
corpus.IndexFiles = indexFilenames
|
|
|
|
if err := corpus.Init(); err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
2015-03-10 05:29:27 -06:00
|
|
|
corpus.IndexDirectory = indexDirectoryDefault
|
2018-10-03 19:38:58 -06:00
|
|
|
corpus.InitVersionInfo()
|
2013-08-01 01:43:31 -06:00
|
|
|
go corpus.RunIndexer()
|
2013-07-16 22:02:35 -06:00
|
|
|
|
2013-07-29 22:23:23 -06:00
|
|
|
pres = godoc.NewPresentation(corpus)
|
|
|
|
pres.TabWidth = 8
|
|
|
|
pres.ShowPlayground = true
|
|
|
|
pres.DeclLinks = true
|
2014-12-23 16:02:05 -07:00
|
|
|
pres.NotesRx = regexp.MustCompile("BUG")
|
2018-09-04 10:55:45 -06:00
|
|
|
pres.GoogleAnalytics = os.Getenv("GODOC_ANALYTICS")
|
2013-07-16 22:02:35 -06:00
|
|
|
|
2018-10-10 20:21:09 -06:00
|
|
|
readTemplates(pres)
|
2015-09-22 23:00:56 -06:00
|
|
|
|
2018-09-04 10:55:45 -06:00
|
|
|
datastoreClient, memcacheClient := getClients()
|
|
|
|
|
|
|
|
// NOTE(cbro): registerHandlers registers itself against DefaultServeMux.
|
|
|
|
// The mux returned has host enforcement, so it's important to register
|
|
|
|
// against this mux and not DefaultServeMux.
|
2015-09-22 20:17:19 -06:00
|
|
|
mux := registerHandlers(pres)
|
2018-09-04 10:55:45 -06:00
|
|
|
dl.RegisterHandlers(mux, datastoreClient, memcacheClient)
|
|
|
|
short.RegisterHandlers(mux, datastoreClient, memcacheClient)
|
2013-07-16 22:02:35 -06:00
|
|
|
|
2015-09-22 23:00:56 -06:00
|
|
|
// 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)
|
|
|
|
|
2018-09-04 10:55:45 -06:00
|
|
|
http.HandleFunc("/_ah/health", func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
io.WriteString(w, "ok")
|
|
|
|
})
|
|
|
|
|
|
|
|
http.HandleFunc("/robots.txt", func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
io.WriteString(w, "User-agent: *\nDisallow: /search\n")
|
|
|
|
})
|
|
|
|
|
|
|
|
if err := redirect.LoadChangeMap("hg-git-mapping.bin"); err != nil {
|
|
|
|
log.Fatalf("LoadChangeMap: %v", err)
|
|
|
|
}
|
|
|
|
|
2013-07-16 22:02:35 -06:00
|
|
|
log.Println("godoc initialization complete")
|
2018-09-04 10:55:45 -06:00
|
|
|
|
|
|
|
// TODO(cbro): add instrumentation via opencensus.
|
|
|
|
port := "8080"
|
|
|
|
if p := os.Getenv("PORT"); p != "" { // PORT is set by GAE flex.
|
|
|
|
port = p
|
|
|
|
}
|
|
|
|
log.Fatal(http.ListenAndServe(":"+port, nil))
|
|
|
|
}
|
|
|
|
|
|
|
|
func getClients() (*datastore.Client, *memcache.Client) {
|
|
|
|
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("GODOC_REDIS_ADDR")
|
|
|
|
if redisAddr == "" {
|
|
|
|
log.Fatalf("Missing redis server for godoc in production mode. set GODOC_REDIS_ADDR environment variable.")
|
|
|
|
}
|
|
|
|
memcacheClient := memcache.New(redisAddr)
|
|
|
|
return datastoreClient, memcacheClient
|
2013-07-16 22:02:35 -06:00
|
|
|
}
|