aboutsummaryrefslogtreecommitdiff
path: root/lib/http
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2024-03-15 14:51:48 +0700
committerShulhan <ms@kilabit.info>2024-03-15 14:53:43 +0700
commit713d51e4792fb873d67d442553c0967e7fa0dc56 (patch)
tree8d06441dd302c253bda3c2cac9a9e3f6694bead9 /lib/http
parent625722e9087173d7b01acfb786de935fe09f4310 (diff)
downloadpakakeh.go-713d51e4792fb873d67d442553c0967e7fa0dc56.tar.xz
lib/http: refactoring FSHandler type to return [*memfs.Node]
Changing FSHandler type to return [*memfs.Node], allow the handler to redirect or return custom node. One of the use case is when service Single Page Application (SPA), where route is handled by JavaScript. For example, when user requested "/dashboard" but dashboard directory does not exist, one can write the following handler to return "/index.html", { node, _ = memfs.Get(`/index.html`) return node }
Diffstat (limited to 'lib/http')
-rw-r--r--lib/http/fs_handler.go12
-rw-r--r--lib/http/http_test.go13
-rw-r--r--lib/http/server.go36
3 files changed, 35 insertions, 26 deletions
diff --git a/lib/http/fs_handler.go b/lib/http/fs_handler.go
index 06d470f1..b88c13b4 100644
--- a/lib/http/fs_handler.go
+++ b/lib/http/fs_handler.go
@@ -12,12 +12,14 @@ import (
// FSHandler define the function to inspect each GET request to Server
// [memfs.MemFS] instance.
-// The node parameter contains the requested file inside the memfs.
+// The node parameter contains the requested file inside the memfs or nil
+// if the file does not exist.
//
-// If the handler return true, server will continue processing the node
-// (writing the [memfs.Node] content type, body, and so on).
+// If the handler return non-nil [*memfs.Node], server will continue
+// processing the node, writing the [memfs.Node] content type, body, and so
+// on.
//
-// If the handler return false, server stop processing the node and return
+// If the handler return nil, server stop processing the node and return
// immediately, which means the function should have already handle writing
// the header, status code, and/or body.
-type FSHandler func(node *memfs.Node, res http.ResponseWriter, req *http.Request) bool
+type FSHandler func(node *memfs.Node, res http.ResponseWriter, req *http.Request) (out *memfs.Node)
diff --git a/lib/http/http_test.go b/lib/http/http_test.go
index c6872fe4..4cb40856 100644
--- a/lib/http/http_test.go
+++ b/lib/http/http_test.go
@@ -106,7 +106,12 @@ var (
// If the node path is start with "/auth/" and cookie name "sid" exist
// with value "authz" it will return true;
// otherwise it will redirect to "/" and return false.
-func handleFS(node *memfs.Node, res http.ResponseWriter, req *http.Request) bool {
+func handleFS(node *memfs.Node, res http.ResponseWriter, req *http.Request) *memfs.Node {
+ if node == nil {
+ res.WriteHeader(http.StatusNotFound)
+ return nil
+ }
+
var (
lowerPath = strings.ToLower(node.Path)
@@ -117,14 +122,14 @@ func handleFS(node *memfs.Node, res http.ResponseWriter, req *http.Request) bool
cookieSid, err = req.Cookie("sid")
if err != nil {
http.Redirect(res, req, "/", http.StatusSeeOther)
- return false
+ return nil
}
if cookieSid.Value != "authz" {
http.Redirect(res, req, "/", http.StatusSeeOther)
- return false
+ return nil
}
}
- return true
+ return node
}
func registerEndpoints() {
diff --git a/lib/http/server.go b/lib/http/server.go
index 6cf8c70c..37ff71dc 100644
--- a/lib/http/server.go
+++ b/lib/http/server.go
@@ -419,22 +419,19 @@ func (srv *Server) HandleFS(res http.ResponseWriter, req *http.Request) {
var (
logp = "HandleFS"
- node *memfs.Node
- responseETag string
- requestETag string
- size int64
- err error
- isDir bool
- ok bool
+ node *memfs.Node
+ err error
+ isDir bool
)
node, isDir = srv.getFSNode(req.URL.Path)
if node == nil {
- res.WriteHeader(http.StatusNotFound)
- return
- }
-
- if isDir && req.URL.Path[len(req.URL.Path)-1] != '/' {
+ if srv.Options.HandleFS == nil {
+ res.WriteHeader(http.StatusNotFound)
+ return
+ }
+ // Fallthrough, call HandleFS below.
+ } else if isDir && req.URL.Path[len(req.URL.Path)-1] != '/' {
// If request path is a directory and it is not end with
// slash, redirect request to location with slash to allow
// relative links works inside the HTML content.
@@ -447,8 +444,8 @@ func (srv *Server) HandleFS(res http.ResponseWriter, req *http.Request) {
}
if srv.Options.HandleFS != nil {
- ok = srv.Options.HandleFS(node, res, req)
- if !ok {
+ node = srv.Options.HandleFS(node, res, req)
+ if node == nil {
return
}
}
@@ -457,8 +454,10 @@ func (srv *Server) HandleFS(res http.ResponseWriter, req *http.Request) {
var nodeModtime = node.ModTime().Unix()
- responseETag = strconv.FormatInt(nodeModtime, 10)
- requestETag = req.Header.Get(HeaderIfNoneMatch)
+ var (
+ responseETag = strconv.FormatInt(nodeModtime, 10)
+ requestETag = req.Header.Get(HeaderIfNoneMatch)
+ )
if requestETag == responseETag {
res.WriteHeader(http.StatusNotModified)
return
@@ -477,7 +476,10 @@ func (srv *Server) HandleFS(res http.ResponseWriter, req *http.Request) {
}
}
- var bodyReader io.ReadSeeker
+ var (
+ bodyReader io.ReadSeeker
+ size int64
+ )
if len(node.Content) > 0 {
bodyReader = bytes.NewReader(node.Content)