mirror of
https://code.rocketnine.space/tslocum/twins.git
synced 2024-11-27 11:38:14 +01:00
parent
d1f9c6d4aa
commit
e7cebf095e
4 changed files with 114 additions and 21 deletions
|
@ -68,6 +68,8 @@ When accessing a directory the file `index.gemini` or `index.gmi` is served.
|
||||||
|
|
||||||
Serve static files from specified root directory.
|
Serve static files from specified root directory.
|
||||||
|
|
||||||
|
Directory listing may be enabled by adding `listdirectory: true`.
|
||||||
|
|
||||||
#### Proxy
|
#### Proxy
|
||||||
|
|
||||||
Forward request to Gemini server at specified URL.
|
Forward request to Gemini server at specified URL.
|
||||||
|
@ -96,6 +98,7 @@ hosts:
|
||||||
-
|
-
|
||||||
path: /sites
|
path: /sites
|
||||||
root: /home/gemini.rocks/data/sites
|
root: /home/gemini.rocks/data/sites
|
||||||
|
listdirectory: true
|
||||||
-
|
-
|
||||||
path: ^/(help|info)$
|
path: ^/(help|info)$
|
||||||
root: /home/gemini.rocks/data/help
|
root: /home/gemini.rocks/data/help
|
||||||
|
|
|
@ -22,6 +22,9 @@ type pathConfig struct {
|
||||||
Proxy string
|
Proxy string
|
||||||
Command string
|
Command string
|
||||||
|
|
||||||
|
// List directory entries
|
||||||
|
ListDirectory bool
|
||||||
|
|
||||||
r *regexp.Regexp
|
r *regexp.Regexp
|
||||||
cmd []string
|
cmd []string
|
||||||
}
|
}
|
||||||
|
|
112
server.go
112
server.go
|
@ -12,6 +12,8 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -48,7 +50,66 @@ func writeStatus(c net.Conn, code int) {
|
||||||
writeHeader(c, code, meta)
|
writeHeader(c, code, meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
func serveFile(c net.Conn, requestData, filePath string) {
|
func serveDirectory(c net.Conn, request *url.URL, dirPath string) {
|
||||||
|
var files []os.FileInfo
|
||||||
|
err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if path == dirPath {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
files = append(files, info)
|
||||||
|
if info.IsDir() {
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
writeStatus(c, gemini.StatusTemporaryFailure)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// List directories first
|
||||||
|
sort.Slice(files, func(i, j int) bool {
|
||||||
|
iDir := files[i].IsDir() || files[i].Mode()&os.ModeSymlink != 0
|
||||||
|
jDir := files[j].IsDir() || files[j].Mode()&os.ModeSymlink != 0
|
||||||
|
if iDir != jDir {
|
||||||
|
return iDir
|
||||||
|
}
|
||||||
|
return i < j
|
||||||
|
})
|
||||||
|
|
||||||
|
writeHeader(c, gemini.StatusSuccess, "text/gemini; charset=utf-8")
|
||||||
|
|
||||||
|
c.Write([]byte("# " + request.Path + "\r\n\r\n"))
|
||||||
|
|
||||||
|
if request.Path != "/" {
|
||||||
|
c.Write([]byte("=> ../ ../\r\n\r\n"))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, info := range files {
|
||||||
|
fileName := info.Name()
|
||||||
|
filePath := url.PathEscape(info.Name())
|
||||||
|
if info.IsDir() || info.Mode()&os.ModeSymlink != 0 {
|
||||||
|
fileName += "/"
|
||||||
|
filePath += "/"
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Write([]byte("=> " + fileName + " " + filePath + "\r\n"))
|
||||||
|
|
||||||
|
if info.IsDir() || info.Mode()&os.ModeSymlink != 0 {
|
||||||
|
c.Write([]byte("\r\n"))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
modified := "Never"
|
||||||
|
if !info.ModTime().IsZero() {
|
||||||
|
modified = info.ModTime().Format("2006-01-02 3:04 PM")
|
||||||
|
}
|
||||||
|
c.Write([]byte(modified + " - " + formatFileSize(info.Size()) + "\r\n\r\n"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func serveFile(c net.Conn, request *url.URL, requestData, filePath string, listDir bool) {
|
||||||
fi, err := os.Stat(filePath)
|
fi, err := os.Stat(filePath)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
writeStatus(c, gemini.StatusNotFound)
|
writeStatus(c, gemini.StatusNotFound)
|
||||||
|
@ -58,6 +119,9 @@ func serveFile(c net.Conn, requestData, filePath string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
originalPath := filePath
|
||||||
|
|
||||||
|
var fetchIndex bool
|
||||||
if mode := fi.Mode(); mode.IsDir() {
|
if mode := fi.Mode(); mode.IsDir() {
|
||||||
if requestData[len(requestData)-1] != '/' {
|
if requestData[len(requestData)-1] != '/' {
|
||||||
// Add trailing slash
|
// Add trailing slash
|
||||||
|
@ -66,6 +130,8 @@ func serveFile(c net.Conn, requestData, filePath string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fetchIndex = true
|
||||||
|
|
||||||
_, err := os.Stat(path.Join(filePath, "index.gemini"))
|
_, err := os.Stat(path.Join(filePath, "index.gemini"))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
filePath = path.Join(filePath, "index.gemini")
|
filePath = path.Join(filePath, "index.gemini")
|
||||||
|
@ -76,6 +142,10 @@ func serveFile(c net.Conn, requestData, filePath string) {
|
||||||
|
|
||||||
fi, err = os.Stat(filePath)
|
fi, err = os.Stat(filePath)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
|
if fetchIndex && listDir {
|
||||||
|
serveDirectory(c, request, originalPath)
|
||||||
|
return
|
||||||
|
}
|
||||||
writeStatus(c, gemini.StatusNotFound)
|
writeStatus(c, gemini.StatusNotFound)
|
||||||
return
|
return
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
|
@ -268,29 +338,29 @@ func handleConn(c net.Conn) {
|
||||||
matchedHost = true
|
matchedHost = true
|
||||||
|
|
||||||
for _, serve := range config.Hosts[hostname] {
|
for _, serve := range config.Hosts[hostname] {
|
||||||
if serve.Proxy != "" {
|
|
||||||
if serve.r != nil && serve.r.Match(pathBytes) {
|
|
||||||
serveProxy(c, requestData, serve.Proxy)
|
|
||||||
return
|
|
||||||
} else if serve.r == nil && strings.HasPrefix(request.Path, serve.Path) {
|
|
||||||
serveProxy(c, requestData, serve.Proxy)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else if serve.cmd != nil {
|
|
||||||
if serve.r != nil && serve.r.Match(pathBytes) {
|
|
||||||
serveCommand(c, serve.cmd)
|
|
||||||
return
|
|
||||||
} else if serve.r == nil && strings.HasPrefix(request.Path, serve.Path) {
|
|
||||||
serveCommand(c, serve.cmd)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if serve.r != nil && serve.r.Match(pathBytes) {
|
if serve.r != nil && serve.r.Match(pathBytes) {
|
||||||
serveFile(c, requestData, path.Join(serve.Root, strippedPath))
|
if serve.Proxy != "" {
|
||||||
|
serveProxy(c, requestData, serve.Proxy)
|
||||||
|
return
|
||||||
|
} else if serve.cmd != nil {
|
||||||
|
serveCommand(c, serve.cmd)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
serveFile(c, request, requestData, path.Join(serve.Root, strippedPath), serve.ListDirectory)
|
||||||
return
|
return
|
||||||
} else if serve.r == nil && strings.HasPrefix(request.Path, serve.Path) {
|
} else if serve.r == nil && strings.HasPrefix(request.Path, serve.Path) {
|
||||||
serveFile(c, requestData, path.Join(serve.Root, strippedPath[len(serve.Path)-1:]))
|
if serve.Proxy != "" {
|
||||||
|
serveProxy(c, requestData, serve.Proxy)
|
||||||
|
return
|
||||||
|
} else if serve.cmd != nil {
|
||||||
|
serveCommand(c, serve.cmd)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
filePath := request.Path[len(serve.Path):]
|
||||||
|
if len(filePath) > 0 && filePath[0] == '/' {
|
||||||
|
filePath = filePath[1:]
|
||||||
|
}
|
||||||
|
serveFile(c, request, requestData, path.Join(serve.Root, filePath), serve.ListDirectory)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
17
util.go
Normal file
17
util.go
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func formatFileSize(b int64) string {
|
||||||
|
const unit = 1000
|
||||||
|
if b < unit {
|
||||||
|
return fmt.Sprintf("%d B", b)
|
||||||
|
}
|
||||||
|
div, exp := int64(unit), 0
|
||||||
|
for n := b / unit; n >= unit; n /= unit {
|
||||||
|
div *= unit
|
||||||
|
exp++
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%.0f %cB",
|
||||||
|
float64(b)/float64(div), "KMGTPE"[exp])
|
||||||
|
}
|
Loading…
Reference in a new issue