Redirect requests with invalid trailing slash
This commit is contained in:
parent
2b7e21666b
commit
0b744eba7e
7 changed files with 78 additions and 63 deletions
|
@ -65,7 +65,7 @@ and the private key file at `certs/live/$DOMAIN/privkey.pem` to twins.
|
|||
### DisableSize
|
||||
|
||||
The size of the response body is included in the media type header by default.
|
||||
Set this option to `true` to disable this feature.
|
||||
Set this option to `true` to disable this feature.
|
||||
|
||||
### Path
|
||||
|
||||
|
|
|
@ -126,8 +126,6 @@ func readconfig(configPath string) error {
|
|||
|
||||
if serve.Path[0] == '^' {
|
||||
serve.r = regexp.MustCompile(serve.Path)
|
||||
} else if serve.Path[len(serve.Path)-1] == '/' {
|
||||
serve.Path = serve.Path[:len(serve.Path)-1]
|
||||
}
|
||||
|
||||
if serve.FastCGI != "" {
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
@ -54,26 +53,18 @@ func serveDirList(c net.Conn, request *url.URL, dirPath string) {
|
|||
writeHeader(c, statusSuccess, "text/gemini; charset=utf-8")
|
||||
|
||||
fmt.Fprintf(c, "# %s\r\n", request.Path)
|
||||
if numDirs > 0 || numFiles > 0 {
|
||||
if numDirs > 0 {
|
||||
if numDirs == 1 {
|
||||
c.Write([]byte("1 directory"))
|
||||
} else {
|
||||
fmt.Fprintf(c, "%d directories", numDirs)
|
||||
}
|
||||
}
|
||||
if numFiles > 0 {
|
||||
if numDirs > 0 {
|
||||
c.Write([]byte(" and "))
|
||||
}
|
||||
if numDirs == 1 {
|
||||
c.Write([]byte("1 file"))
|
||||
} else {
|
||||
fmt.Fprintf(c, "%d files", numFiles)
|
||||
}
|
||||
}
|
||||
c.Write([]byte("\r\n\n"))
|
||||
if numDirs == 1 {
|
||||
c.Write([]byte("1 directory"))
|
||||
} else {
|
||||
fmt.Fprintf(c, "%d directories", numDirs)
|
||||
}
|
||||
c.Write([]byte(", "))
|
||||
if numDirs == 1 {
|
||||
c.Write([]byte("1 file"))
|
||||
} else {
|
||||
fmt.Fprintf(c, "%d files", numFiles)
|
||||
}
|
||||
c.Write([]byte("\r\n\n"))
|
||||
|
||||
if request.Path != "/" {
|
||||
c.Write([]byte("=> ../ ../\r\n\r\n"))
|
||||
|
@ -102,37 +93,7 @@ func serveDirList(c net.Conn, request *url.URL, dirPath string) {
|
|||
}
|
||||
}
|
||||
|
||||
func serveFile(c net.Conn, request *url.URL, filePath string, listDir bool) {
|
||||
fi, err := os.Stat(filePath)
|
||||
if err != nil {
|
||||
writeStatus(c, statusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
if mode := fi.Mode(); mode.IsDir() {
|
||||
if len(request.Path) == 0 || request.Path[len(request.Path)-1] != '/' {
|
||||
// Add trailing slash
|
||||
writeHeader(c, statusRedirectPermanent, request.String()+"/")
|
||||
return
|
||||
}
|
||||
|
||||
_, err := os.Stat(path.Join(filePath, "index.gmi"))
|
||||
if err != nil {
|
||||
_, err := os.Stat(path.Join(filePath, "index.gemini"))
|
||||
if err != nil {
|
||||
if listDir {
|
||||
serveDirList(c, request, filePath)
|
||||
return
|
||||
}
|
||||
writeStatus(c, statusNotFound)
|
||||
return
|
||||
}
|
||||
filePath = path.Join(filePath, "index.gemini")
|
||||
} else {
|
||||
filePath = path.Join(filePath, "index.gmi")
|
||||
}
|
||||
}
|
||||
|
||||
func serveFile(c net.Conn, filePath string) {
|
||||
// Open file
|
||||
file, _ := os.Open(filePath)
|
||||
defer file.Close()
|
74
server.go
74
server.go
|
@ -9,6 +9,7 @@ import (
|
|||
"log"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"strconv"
|
||||
|
@ -105,24 +106,42 @@ func servePath(c *tls.Conn, request *url.URL, serve *pathConfig) {
|
|||
resolvedPath := request.Path
|
||||
requestSplit := strings.Split(request.Path, "/")
|
||||
pathSlashes := len(slashesRegexp.FindAllStringIndex(serve.Path, -1))
|
||||
if len(request.Path) > 0 && request.Path[0] == '/' {
|
||||
pathSlashes++ // Regexp does not match starting slash
|
||||
if len(serve.Path) > 0 {
|
||||
if serve.Path[0] == '/' {
|
||||
pathSlashes++ // Regexp does not match starting slash
|
||||
}
|
||||
if serve.Path[len(serve.Path)-1] != '/' {
|
||||
pathSlashes++
|
||||
}
|
||||
}
|
||||
if len(requestSplit) >= pathSlashes+1 {
|
||||
resolvedPath = "/" + strings.Join(requestSplit[pathSlashes+1:], "/")
|
||||
if len(requestSplit) >= pathSlashes {
|
||||
resolvedPath = strings.Join(requestSplit[pathSlashes:], "/")
|
||||
}
|
||||
|
||||
var filePath string
|
||||
if serve.Root != "" {
|
||||
root := serve.Root
|
||||
if root[len(root)-1] != '/' {
|
||||
root += "/"
|
||||
}
|
||||
filePath = path.Join(root, resolvedPath)
|
||||
}
|
||||
|
||||
if serve.Proxy != "" {
|
||||
serveProxy(c, request, serve.Proxy)
|
||||
return
|
||||
} else if serve.FastCGI != "" {
|
||||
if filePath == "" {
|
||||
writeStatus(c, statusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
contentType := "text/gemini; charset=utf-8"
|
||||
if serve.Type != "" {
|
||||
contentType = serve.Type
|
||||
}
|
||||
writeHeader(c, statusSuccess, contentType)
|
||||
|
||||
filePath := path.Join(serve.Root, request.Path[1:])
|
||||
serveFastCGI(c, config.fcgiPools[serve.FastCGI], request, filePath)
|
||||
return
|
||||
} else if serve.cmd != nil {
|
||||
|
@ -137,11 +156,48 @@ func servePath(c *tls.Conn, request *url.URL, serve *pathConfig) {
|
|||
serveCommand(c, request, serve.cmd)
|
||||
return
|
||||
}
|
||||
filePath := resolvedPath
|
||||
if len(filePath) > 0 && filePath[0] == '/' {
|
||||
filePath = filePath[1:]
|
||||
|
||||
if filePath == "" {
|
||||
writeStatus(c, statusNotFound)
|
||||
return
|
||||
}
|
||||
serveFile(c, request, path.Join(serve.Root, filePath), serve.ListDirectory)
|
||||
|
||||
fi, err := os.Stat(filePath)
|
||||
if err != nil {
|
||||
writeStatus(c, statusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
mode := fi.Mode()
|
||||
hasTrailingSlash := len(request.Path) > 0 && request.Path[len(request.Path)-1] == '/'
|
||||
if mode.IsDir() {
|
||||
if !hasTrailingSlash {
|
||||
writeHeader(c, statusRedirectPermanent, request.String()+"/")
|
||||
return
|
||||
}
|
||||
|
||||
_, err := os.Stat(path.Join(filePath, "index.gmi"))
|
||||
if err != nil {
|
||||
_, err := os.Stat(path.Join(filePath, "index.gemini"))
|
||||
if err != nil {
|
||||
if serve.ListDirectory {
|
||||
serveDirList(c, request, filePath)
|
||||
return
|
||||
}
|
||||
writeStatus(c, statusNotFound)
|
||||
return
|
||||
}
|
||||
filePath = path.Join(filePath, "index.gemini")
|
||||
} else {
|
||||
filePath = path.Join(filePath, "index.gmi")
|
||||
}
|
||||
} else if hasTrailingSlash && len(request.Path) > 1 {
|
||||
r := request.String()
|
||||
writeHeader(c, statusRedirectPermanent, r[:len(r)-1])
|
||||
return
|
||||
}
|
||||
|
||||
serveFile(c, filePath)
|
||||
}
|
||||
|
||||
func serveConn(c *tls.Conn) {
|
||||
|
|
Loading…
Reference in a new issue