aboutsummaryrefslogtreecommitdiff
path: root/lib/http/server.go
diff options
context:
space:
mode:
Diffstat (limited to 'lib/http/server.go')
-rw-r--r--lib/http/server.go107
1 files changed, 47 insertions, 60 deletions
diff --git a/lib/http/server.go b/lib/http/server.go
index 76713308..edc99cf7 100644
--- a/lib/http/server.go
+++ b/lib/http/server.go
@@ -12,6 +12,7 @@ import (
"io"
"log"
"net/http"
+ "net/url"
"os"
"path"
"sort"
@@ -384,60 +385,67 @@ func (srv *Server) Stop(wait time.Duration) (err error) {
// getFSNode get the memfs Node based on the request path.
//
-// If the path is not exist, try path with index.html;
-// if it still not exist try path with suffix .html.
+// If the path is not exist, try path with ".html".
//
-// If the path is directory and contains index.html, the node for index.html
-// with true will be returned.
+// If the path is directory and contains index.html, the node content for
+// index.html will be returned.
//
// If the path is directory and does not contains index.html and
// [ServerOptions.EnableIndexHTML] is true, server will generate list of
// content for index.html.
-func (srv *Server) getFSNode(reqPath string) (node *memfs.Node) {
+//
+// The redirectPath return whether the server should redirect first before
+// serving the content to provide consistent canonical path that end with "/"
+// for directory that contains "index.html" or EnableIndexHTML is on.
+func (srv *Server) getFSNode(reqURL url.URL) (node *memfs.Node, redirectURL *url.URL) {
if srv.Options.Memfs == nil {
- return nil
+ return nil, nil
}
+ reqPath := reqURL.Path
+ endWithSlash := reqPath[len(reqPath)-1] == '/'
var err error
+ // The [MemFS.Get] method check reqPath with trailing `/` removed, so
+ // "path" or "path/" will return the same node.
node, err = srv.Options.Memfs.Get(reqPath)
if err != nil {
if !errors.Is(err, os.ErrNotExist) {
- return nil
+ return nil, nil
}
-
- var pathHTML = path.Join(reqPath, `index.html`)
-
- node, err = srv.Options.Memfs.Get(pathHTML)
- if err != nil {
- pathHTML = reqPath + `.html`
+ if !endWithSlash {
+ pathHTML := reqPath + `.html`
node, err = srv.Options.Memfs.Get(pathHTML)
if err != nil {
- return nil
+ return nil, nil
}
- return node
+ return node, nil
}
+ return nil, nil
+ }
+ if !node.IsDir() {
+ return node, nil
}
- if node.IsDir() {
- var (
- pathHTML = path.Join(reqPath, `index.html`)
- nodeIndexHTML *memfs.Node
- )
-
- nodeIndexHTML, err = srv.Options.Memfs.Get(pathHTML)
- if err == nil {
- return nodeIndexHTML
- }
-
- if !srv.Options.EnableIndexHTML {
- return node
+ pathIndexHTML := path.Join(reqPath, `index.html`)
+ nodeIndexHTML, err := srv.Options.Memfs.Get(pathIndexHTML)
+ if err != nil {
+ if srv.Options.EnableIndexHTML {
+ if !endWithSlash {
+ redirectURL = reqURL.JoinPath(`/`)
+ return nil, redirectURL
+ }
+ node.GenerateIndexHTML()
+ return node, nil
}
-
- node.GenerateIndexHTML()
+ return nil, nil
+ }
+ if !endWithSlash {
+ redirectURL = reqURL.JoinPath(`/`)
+ return nil, redirectURL
}
- return node
+ return nodeIndexHTML, nil
}
// handleDelete handle the DELETE request by searching the registered route
@@ -468,32 +476,14 @@ func (srv *Server) handleDelete(res http.ResponseWriter, req *http.Request) {
//
// If the request Path is not exist it will return 404 Not Found.
func (srv *Server) HandleFS(res http.ResponseWriter, req *http.Request) {
- var (
- logp = "HandleFS"
-
- node *memfs.Node
- err error
- )
-
- node = srv.getFSNode(req.URL.Path)
- if node == nil {
- if srv.Options.HandleFS == nil {
- res.WriteHeader(http.StatusNotFound)
- return
- }
- // Fallthrough, call HandleFS below.
- } else if node.IsDir() && req.URL.Path[len(req.URL.Path)-1] != '/' {
+ node, redirectURL := srv.getFSNode(*req.URL)
+ if redirectURL != nil {
// 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.
- var redirectPath = req.URL.Path + "/"
- if len(req.URL.RawQuery) > 0 {
- redirectPath += "?" + req.URL.RawQuery
- }
- http.Redirect(res, req, redirectPath, http.StatusFound)
+ http.Redirect(res, req, redirectURL.String(), http.StatusFound)
return
}
-
if srv.Options.HandleFS != nil {
var statusCode int
node, statusCode = srv.Options.HandleFS(node, res, req)
@@ -522,9 +512,7 @@ func (srv *Server) HandleFS(res http.ResponseWriter, req *http.Request) {
var ifModifiedSince = req.Header.Get(HeaderIfModifiedSince)
if len(ifModifiedSince) != 0 {
- var timeModsince time.Time
-
- timeModsince, err = time.Parse(time.RFC1123, ifModifiedSince)
+ timeModsince, err := time.Parse(time.RFC1123, ifModifiedSince)
if err == nil {
if nodeModtime <= timeModsince.Unix() {
res.WriteHeader(http.StatusNotModified)
@@ -542,8 +530,7 @@ func (srv *Server) HandleFS(res http.ResponseWriter, req *http.Request) {
bodyReader = bytes.NewReader(node.Content)
size = node.Size()
} else {
- var f *os.File
- f, err = os.Open(node.SysPath)
+ f, err := os.Open(node.SysPath)
if err != nil {
res.WriteHeader(http.StatusInternalServerError)
return
@@ -577,7 +564,7 @@ func (srv *Server) HandleFS(res http.ResponseWriter, req *http.Request) {
return
}
- responseWrite(logp, res, req, bodyReader)
+ responseWrite(`HandleFS`, res, req, bodyReader)
}
// handleGet handle the GET request by searching the registered route and
@@ -653,8 +640,8 @@ func (srv *Server) handleHead(res http.ResponseWriter, req *http.Request) {
func (srv *Server) handleOptions(res http.ResponseWriter, req *http.Request) {
methods := make(map[string]bool)
- var node = srv.getFSNode(req.URL.Path)
- if node != nil {
+ node, redirectURL := srv.getFSNode(*req.URL)
+ if node != nil || redirectURL != nil {
methods[http.MethodGet] = true
methods[http.MethodHead] = true
}