File: appinit.go

package info (click to toggle)
golang-golang-x-tools 1%3A0.0~git20190125.d66bd3c%2Bds-4
  • links: PTS, VCS
  • area: main
  • in suites: buster, buster-backports
  • size: 8,912 kB
  • sloc: asm: 1,394; yacc: 155; makefile: 109; sh: 108; ansic: 17; xml: 11
file content (154 lines) | stat: -rw-r--r-- 4,517 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// 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.

// +build golangorg

package main

// This file replaces main.go when running godoc under app-engine.
// See README.godoc-app for details.

import (
	"archive/zip"
	"context"
	"io"
	"log"
	"net/http"
	"os"
	"path"
	"regexp"
	"runtime"
	"strings"

	"golang.org/x/tools/godoc"
	"golang.org/x/tools/godoc/dl"
	"golang.org/x/tools/godoc/proxy"
	"golang.org/x/tools/godoc/redirect"
	"golang.org/x/tools/godoc/short"
	"golang.org/x/tools/godoc/vfs"
	"golang.org/x/tools/godoc/vfs/gatefs"
	"golang.org/x/tools/godoc/vfs/mapfs"
	"golang.org/x/tools/godoc/vfs/zipfs"

	"cloud.google.com/go/datastore"
	"golang.org/x/tools/internal/memcache"
)

func main() {
	log.SetFlags(log.Lshortfile | log.LstdFlags)

	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")
	)

	playEnabled = true

	log.Println("initializing godoc ...")
	log.Printf(".zip file   = %s", zipFilename)
	log.Printf(".zip GOROOT = %s", zipGoroot)
	log.Printf("index files = %s", indexFilenames)

	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)
	}

	fs.Bind("/lib/godoc", vfs.OS("/usr/share/golang-golang-x-tools/godoc/"), "/", vfs.BindReplace)

	corpus := godoc.NewCorpus(fs)
	corpus.Verbose = false
	corpus.MaxResults = 10000 // matches flag default in main.go
	corpus.IndexEnabled = true
	corpus.IndexFiles = indexFilenames
	if err := corpus.Init(); err != nil {
		log.Fatal(err)
	}
	corpus.IndexDirectory = indexDirectoryDefault
	corpus.InitVersionInfo()
	if indexFilenames != "" {
		corpus.RunIndexer()
	} else {
		go corpus.RunIndexer()
	}

	pres = godoc.NewPresentation(corpus)
	pres.TabWidth = 8
	pres.ShowPlayground = true
	pres.DeclLinks = true
	pres.NotesRx = regexp.MustCompile("BUG")
	pres.GoogleAnalytics = os.Getenv("GODOC_ANALYTICS")

	readTemplates(pres)

	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.
	mux := registerHandlers(pres)
	dl.RegisterHandlers(mux, 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)

	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)
	}

	log.Println("godoc initialization complete")

	// 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
}