mirror of
https://code.rocketnine.space/tslocum/twins.git
synced 2024-11-27 14:38:14 +01:00
Redirect requests with invalid trailing slash
This commit is contained in:
parent
2b7e21666b
commit
0b744eba7e
7 changed files with 78 additions and 63 deletions
|
@ -126,8 +126,6 @@ func readconfig(configPath string) error {
|
||||||
|
|
||||||
if serve.Path[0] == '^' {
|
if serve.Path[0] == '^' {
|
||||||
serve.r = regexp.MustCompile(serve.Path)
|
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 != "" {
|
if serve.FastCGI != "" {
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -54,26 +53,18 @@ func serveDirList(c net.Conn, request *url.URL, dirPath string) {
|
||||||
writeHeader(c, statusSuccess, "text/gemini; charset=utf-8")
|
writeHeader(c, statusSuccess, "text/gemini; charset=utf-8")
|
||||||
|
|
||||||
fmt.Fprintf(c, "# %s\r\n", request.Path)
|
fmt.Fprintf(c, "# %s\r\n", request.Path)
|
||||||
if numDirs > 0 || numFiles > 0 {
|
|
||||||
if numDirs > 0 {
|
|
||||||
if numDirs == 1 {
|
if numDirs == 1 {
|
||||||
c.Write([]byte("1 directory"))
|
c.Write([]byte("1 directory"))
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(c, "%d directories", numDirs)
|
fmt.Fprintf(c, "%d directories", numDirs)
|
||||||
}
|
}
|
||||||
}
|
c.Write([]byte(", "))
|
||||||
if numFiles > 0 {
|
|
||||||
if numDirs > 0 {
|
|
||||||
c.Write([]byte(" and "))
|
|
||||||
}
|
|
||||||
if numDirs == 1 {
|
if numDirs == 1 {
|
||||||
c.Write([]byte("1 file"))
|
c.Write([]byte("1 file"))
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(c, "%d files", numFiles)
|
fmt.Fprintf(c, "%d files", numFiles)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
c.Write([]byte("\r\n\n"))
|
c.Write([]byte("\r\n\n"))
|
||||||
}
|
|
||||||
|
|
||||||
if request.Path != "/" {
|
if request.Path != "/" {
|
||||||
c.Write([]byte("=> ../ ../\r\n\r\n"))
|
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) {
|
func serveFile(c net.Conn, filePath string) {
|
||||||
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open file
|
// Open file
|
||||||
file, _ := os.Open(filePath)
|
file, _ := os.Open(filePath)
|
||||||
defer file.Close()
|
defer file.Close()
|
72
server.go
72
server.go
|
@ -9,6 +9,7 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -105,24 +106,42 @@ func servePath(c *tls.Conn, request *url.URL, serve *pathConfig) {
|
||||||
resolvedPath := request.Path
|
resolvedPath := request.Path
|
||||||
requestSplit := strings.Split(request.Path, "/")
|
requestSplit := strings.Split(request.Path, "/")
|
||||||
pathSlashes := len(slashesRegexp.FindAllStringIndex(serve.Path, -1))
|
pathSlashes := len(slashesRegexp.FindAllStringIndex(serve.Path, -1))
|
||||||
if len(request.Path) > 0 && request.Path[0] == '/' {
|
if len(serve.Path) > 0 {
|
||||||
|
if serve.Path[0] == '/' {
|
||||||
pathSlashes++ // Regexp does not match starting slash
|
pathSlashes++ // Regexp does not match starting slash
|
||||||
}
|
}
|
||||||
if len(requestSplit) >= pathSlashes+1 {
|
if serve.Path[len(serve.Path)-1] != '/' {
|
||||||
resolvedPath = "/" + strings.Join(requestSplit[pathSlashes+1:], "/")
|
pathSlashes++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 != "" {
|
if serve.Proxy != "" {
|
||||||
serveProxy(c, request, serve.Proxy)
|
serveProxy(c, request, serve.Proxy)
|
||||||
return
|
return
|
||||||
} else if serve.FastCGI != "" {
|
} else if serve.FastCGI != "" {
|
||||||
|
if filePath == "" {
|
||||||
|
writeStatus(c, statusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
contentType := "text/gemini; charset=utf-8"
|
contentType := "text/gemini; charset=utf-8"
|
||||||
if serve.Type != "" {
|
if serve.Type != "" {
|
||||||
contentType = serve.Type
|
contentType = serve.Type
|
||||||
}
|
}
|
||||||
writeHeader(c, statusSuccess, contentType)
|
writeHeader(c, statusSuccess, contentType)
|
||||||
|
|
||||||
filePath := path.Join(serve.Root, request.Path[1:])
|
|
||||||
serveFastCGI(c, config.fcgiPools[serve.FastCGI], request, filePath)
|
serveFastCGI(c, config.fcgiPools[serve.FastCGI], request, filePath)
|
||||||
return
|
return
|
||||||
} else if serve.cmd != nil {
|
} else if serve.cmd != nil {
|
||||||
|
@ -137,11 +156,48 @@ func servePath(c *tls.Conn, request *url.URL, serve *pathConfig) {
|
||||||
serveCommand(c, request, serve.cmd)
|
serveCommand(c, request, serve.cmd)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
filePath := resolvedPath
|
|
||||||
if len(filePath) > 0 && filePath[0] == '/' {
|
if filePath == "" {
|
||||||
filePath = filePath[1:]
|
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) {
|
func serveConn(c *tls.Conn) {
|
||||||
|
|
Loading…
Reference in a new issue