-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
121 lines (100 loc) · 2.78 KB
/
main.go
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
package main
import (
"flag"
"go/build"
"log"
"net"
"net/textproto"
"path/filepath"
"runtime"
"text/template"
"time"
"golang.org/x/tools/godoc"
"golang.org/x/tools/godoc/static"
"golang.org/x/tools/godoc/vfs"
"golang.org/x/tools/godoc/vfs/mapfs"
)
// Maximum amount of time to wait when reading from a client before
// timing out.
const readTimeout = time.Second * 10
var (
hostFlag = flag.String("host", "localhost", "gopher service hostname")
addrFlag = flag.String("addr", ":gopher", "gopher service address (e.g. ':70')")
gorootFlag = flag.String("goroot", runtime.GOROOT(), "Go root directory")
verboseFlag = flag.Bool("v", false, "verbose mode")
)
func main() {
flag.Parse()
// TODO: Limit concurrent access to filesystem.
fs := vfs.NameSpace{}
rootfs := vfs.OS(*gorootFlag)
fs.Bind("/", rootfs, "/", vfs.BindReplace)
fs.Bind("/lib/godoc", mapfs.New(static.Files), "/", vfs.BindReplace)
// Bind $GOPATH trees into Go root.
for _, p := range filepath.SplitList(build.Default.GOPATH) {
fs.Bind("/src", vfs.OS(p), "/src", vfs.BindAfter)
}
corpus := godoc.NewCorpus(fs)
corpus.Verbose = *verboseFlag
if err := corpus.Init(); err != nil {
log.Fatal(err)
}
pres := godoc.NewPresentation(corpus)
pres.PackageText = readTemplate(fs, pres, "package.txt")
ln, err := net.Listen("tcp", *addrFlag)
if err != nil {
log.Fatal(err)
}
log.Printf("listening on %s", ln.Addr())
handler := &handler{
pres: pres,
host: *hostFlag,
port: getPort(ln.Addr()),
}
for {
conn, err := ln.Accept()
if err != nil {
log.Print(err)
}
go serve(conn, handler)
}
}
// get the port as a number from an address; this allows for callers
// to use named ports in the address.
func getPort(addr net.Addr) int {
checkErr := func(err error) {
if err != nil {
log.Fatal(err)
}
}
_, portStr, err := net.SplitHostPort(addr.String())
checkErr(err)
port, err := net.LookupPort(addr.Network(), portStr)
checkErr(err)
return port
}
func readTemplate(fs vfs.FileSystem, pres *godoc.Presentation, name string) *template.Template {
path := "lib/godoc/" + name
// template package can't read directly from a vfs, so we need
// to read the data ourselves.
data, err := vfs.ReadFile(fs, path)
if err != nil {
log.Fatal("readTemplate: ", err)
}
return template.Must(template.New(name).Funcs(pres.FuncMap()).Parse(string(data)))
}
// Serve a new connection.
func serve(conn net.Conn, handler *handler) {
defer func() {
if err := recover(); err != nil {
const size = 64 << 10
buf := make([]byte, size)
buf = buf[:runtime.Stack(buf, false)]
log.Printf("panic serving %v: %v\n%s", conn.RemoteAddr(), err, buf)
}
conn.Close()
}()
// Don't let clients hog connections forever.
conn.SetReadDeadline(time.Now().Add(readTimeout))
handler.Handle(textproto.NewConn(conn))
}