-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
169 lines (140 loc) · 3.91 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
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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
package main
import (
"bytes"
"encoding/json"
"go/ast"
"go/parser"
"go/printer"
"go/token"
"golang.org/x/tools/imports"
"html/template"
"log"
"net/http"
"net/url"
"os"
"path/filepath"
"strings"
"time"
)
const (
ServerModeDebug = "debug"
ServerModeProd = "production"
TransportSocket = "SocketTransport"
TransportHttp = "HTTPTransport"
)
// socketAddr returns the WebSocket handler address.
var socketAddr = func() string { return "ws://" + host + "/socket" }
// analyticsHTML is optional analytics HTML to insert at the beginning of <head>.
var analyticsHTML = template.HTML(os.Getenv("ANALYTICS_HTML"))
var host = "127.0.0.1"
var port = "3999"
var serverMode = ServerModeDebug
const root = "."
const localhostWarning = `
WARNING! WARNING! WARNING!
The tour server appears to be listening on an address that is
not localhost and is configured to run code snippets locally.
Anyone with access to this address and port will have access
to this machine as the user running gotour.
If you don't understand this message, hit Control-C to terminate this process.
WARNING! WARNING! WARNING!
`
func main() {
if os.Getenv("HOST") != "" {
host = os.Getenv("HOST")
}
if os.Getenv("PORT") != "" {
port = os.Getenv("PORT")
}
if err := initTour(TransportSocket); err != nil {
log.Fatal(err)
}
registerHandler()
log.Println("Serving at http://" + host + ":" + port)
log.Fatal(http.ListenAndServe(":"+port, nil))
}
// 注册路由
func registerHandler() {
// Keep these static file handlers in sync with app.yaml.
http.Handle("/favicon.ico", http.FileServer(http.Dir(filepath.Join(root, "static", "img"))))
static := http.FileServer(http.Dir(root))
http.Handle("/content/img/", static)
http.Handle("/static/", static)
http.HandleFunc("/script.js", jsHandler())
// content
http.HandleFunc("/", rootHandler())
http.HandleFunc("/lesson/", lessonHandler())
http.HandleFunc("/fmt", fmtHandler())
// socket
origin := &url.URL{Scheme: "http", Host: host + ":" + port}
http.Handle("/socket", NewRunHandler(origin))
}
// rootHandler returns a handler for all the requests except the ones for lessons.
func rootHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if err := renderUI(w); err != nil {
log.Println(err)
}
}
}
func jsHandler() http.HandlerFunc {
var modTime = time.Now()
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-type", "application/javascript")
// Set expiration time in one week. todo 开发阶段关缓存
//w.Header().Set("Cache-control", "max-age=604800")
http.ServeContent(w, r, "", modTime, bytes.NewReader(scriptContent.Bytes()))
}
}
// lessonHandler handler the HTTP requests for lessons.
func lessonHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
lesson := strings.TrimPrefix(r.URL.Path, "/lesson/")
if err := writeLesson(lesson, w); err != nil {
if err == lessonNotFound {
http.NotFound(w, r)
} else {
log.Println(err)
}
}
}
}
type fmtResponse struct {
Body string
Error string
}
func fmtHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
resp := new(fmtResponse)
var body string
var err error
if r.FormValue("imports") == "true" {
var b []byte
b, err = imports.Process("prog.go", []byte(r.FormValue("body")), nil)
body = string(b)
} else {
body, err = gofmt(r.FormValue("body"))
}
if err != nil {
resp.Error = err.Error()
} else {
resp.Body = body
}
json.NewEncoder(w).Encode(resp)
}
}
func gofmt(body string) (string, error) {
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, "prog.go", body, parser.ParseComments)
if err != nil {
return "", err
}
ast.SortImports(fset, f)
var buf bytes.Buffer
config := &printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 8}
err = config.Fprint(&buf, fset, f)
if err != nil {
return "", err
}
return buf.String(), nil
}