Skip to content

Commit 25fb813

Browse files
committed
server: add SPA static router
1 parent 32c56bc commit 25fb813

File tree

2 files changed

+71
-1
lines changed

2 files changed

+71
-1
lines changed

cmd/playground/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ func start(packagesFile, addr, goRoot string, debug bool) error {
6262

6363
r := mux.NewRouter()
6464
langserver.New(packages).Mount(r.PathPrefix("/api").Subrouter())
65-
r.PathPrefix("/").Handler(http.FileServer(http.Dir("./public")))
65+
r.PathPrefix("/").Handler(langserver.SpaFileServer("./public"))
6666

6767
zap.S().Infof("Listening on %q", addr)
6868

pkg/langserver/spa.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package langserver
2+
3+
import (
4+
"net/http"
5+
"os"
6+
"path"
7+
"path/filepath"
8+
"strings"
9+
)
10+
11+
// Advanced static server
12+
type spaFileServer struct {
13+
root http.Dir
14+
NotFoundHandler func(http.ResponseWriter, *http.Request)
15+
}
16+
17+
func (fs *spaFileServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
18+
19+
if containsDotDot(r.URL.Path) {
20+
Errorf(http.StatusBadRequest, "Bad Request").Write(w)
21+
return
22+
}
23+
24+
//if empty, set current directory
25+
dir := string(fs.root)
26+
if dir == "" {
27+
dir = "."
28+
}
29+
30+
//add prefix and clean
31+
upath := r.URL.Path
32+
if !strings.HasPrefix(upath, "/") {
33+
upath = "/" + upath
34+
r.URL.Path = upath
35+
}
36+
upath = path.Clean(upath)
37+
38+
//path to file
39+
name := path.Join(dir, filepath.FromSlash(upath))
40+
41+
//check if file exists
42+
f, err := os.Open(name)
43+
if err != nil {
44+
if os.IsNotExist(err) {
45+
Errorf(http.StatusNotFound, "Not Found").Write(w)
46+
return
47+
}
48+
}
49+
defer f.Close()
50+
51+
http.ServeFile(w, r, name)
52+
}
53+
54+
func containsDotDot(v string) bool {
55+
if !strings.Contains(v, "..") {
56+
return false
57+
}
58+
for _, ent := range strings.FieldsFunc(v, isSlashRune) {
59+
if ent == ".." {
60+
return true
61+
}
62+
}
63+
return false
64+
}
65+
66+
func isSlashRune(r rune) bool { return r == '/' || r == '\\' }
67+
68+
func SpaFileServer(root http.Dir) http.Handler {
69+
return &spaFileServer{root: root}
70+
}

0 commit comments

Comments
 (0)