Skip to content

Commit 56d9fe1

Browse files
committed
Add a customized httpx Router
1 parent 2ebc01d commit 56d9fe1

File tree

2 files changed

+113
-0
lines changed

2 files changed

+113
-0
lines changed

router/filehandler.go

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package router
2+
3+
import (
4+
"net/http"
5+
"strings"
6+
"sync"
7+
)
8+
9+
// fileHandler returns a middleware that serves files from the given file system.
10+
func fileHandler(path string, fs http.FileSystem) func(http.HandlerFunc) http.HandlerFunc {
11+
fileServer := http.FileServer(fs)
12+
pathWithoutTrailSlash := ensureNoTrailingSlash(path)
13+
canServe := createServeChecker(path, fs)
14+
15+
return func(next http.HandlerFunc) http.HandlerFunc {
16+
return func(w http.ResponseWriter, r *http.Request) {
17+
if canServe(r) {
18+
r.URL.Path = r.URL.Path[len(pathWithoutTrailSlash):]
19+
// no-cache allows caches to store a response but requires them to revalidate it before reuse.
20+
w.Header().Set("Cache-Control", "no-cache")
21+
w.Header().Set("Vary", "Origin, Cache-Control")
22+
fileServer.ServeHTTP(w, r)
23+
} else {
24+
next(w, r)
25+
}
26+
}
27+
}
28+
}
29+
30+
func createFileChecker(fs http.FileSystem) func(string) bool {
31+
var lock sync.RWMutex
32+
fileChecker := make(map[string]bool)
33+
34+
return func(path string) bool {
35+
lock.RLock()
36+
exist, ok := fileChecker[path]
37+
lock.RUnlock()
38+
if ok {
39+
return exist
40+
}
41+
42+
lock.Lock()
43+
defer lock.Unlock()
44+
45+
file, err := fs.Open(path)
46+
exist = err == nil
47+
fileChecker[path] = exist
48+
if err != nil {
49+
return false
50+
}
51+
52+
_ = file.Close()
53+
return true
54+
}
55+
}
56+
57+
func createServeChecker(path string, fs http.FileSystem) func(r *http.Request) bool {
58+
pathWithTrailSlash := ensureTrailingSlash(path)
59+
fileChecker := createFileChecker(fs)
60+
61+
return func(r *http.Request) bool {
62+
return r.Method == http.MethodGet &&
63+
strings.HasPrefix(r.URL.Path, pathWithTrailSlash) &&
64+
fileChecker(r.URL.Path[len(pathWithTrailSlash):])
65+
}
66+
}
67+
68+
func ensureTrailingSlash(path string) string {
69+
if strings.HasSuffix(path, "/") {
70+
return path
71+
}
72+
73+
return path + "/"
74+
}
75+
76+
func ensureNoTrailingSlash(path string) string {
77+
if strings.HasSuffix(path, "/") {
78+
return path[:len(path)-1]
79+
}
80+
81+
return path
82+
}

router/router.go

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package router
2+
3+
import (
4+
"net/http"
5+
6+
"github.com/zeromicro/go-zero/rest"
7+
"github.com/zeromicro/go-zero/rest/httpx"
8+
"github.com/zeromicro/go-zero/rest/router"
9+
)
10+
11+
// Router returns a customized httpx Router
12+
func New(path string, fs http.FileSystem) httpx.Router {
13+
rt := router.NewRouter()
14+
return newFileServingRouter(rt, path, fs)
15+
}
16+
17+
type fileServingRouter struct {
18+
httpx.Router
19+
middleware rest.Middleware
20+
}
21+
22+
func newFileServingRouter(router httpx.Router, path string, fs http.FileSystem) httpx.Router {
23+
return &fileServingRouter{
24+
Router: router,
25+
middleware: fileHandler(path, fs),
26+
}
27+
}
28+
29+
func (f *fileServingRouter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
30+
f.middleware(f.Router.ServeHTTP)(w, r)
31+
}

0 commit comments

Comments
 (0)