Support directory listing via HTTPS
This commit is contained in:
parent
8d6cb6527e
commit
faab032d45
4 changed files with 91 additions and 72 deletions
|
@ -219,17 +219,17 @@ hosts:
|
|||
key: /srv/gemini.rocks/data/cert.key
|
||||
paths:
|
||||
-
|
||||
path: ^/sites/.*\.php$
|
||||
root: /home/geminirocks/data
|
||||
path: ^/.*\.php$
|
||||
root: /home/geminirocks/public_html
|
||||
fastcgi: unix:///var/run/php.sock
|
||||
-
|
||||
path: /sites
|
||||
root: /home/geminirocks/data
|
||||
path: /files
|
||||
root: /home/geminirocks/files
|
||||
cache: 604800 # Cache for 1 week
|
||||
list: true # Enable directory listing
|
||||
-
|
||||
path: ^/(help|info)$
|
||||
root: /home/geminirocks/data/help
|
||||
root: /home/geminirocks/docs/help
|
||||
-
|
||||
path: ^/proxy-example$
|
||||
proxy: gemini://localhost:1966
|
||||
|
@ -237,6 +237,13 @@ hosts:
|
|||
path: ^/cmd-example$
|
||||
command: uname -a
|
||||
cache: 0 # Do not cache
|
||||
-
|
||||
path: /
|
||||
root: /home/geminirocks/public_html
|
||||
twins.rocketnine.space:
|
||||
cert: /srv/twins.rocketnine.space/data/cert.crt
|
||||
key: /srv/twins.rocketnine.space/data/cert.key
|
||||
paths:
|
||||
-
|
||||
path: /redir-path-example
|
||||
redirect: /other-resource
|
||||
|
@ -245,15 +252,5 @@ hosts:
|
|||
redirect: gemini://gemini.circumlunar.space/
|
||||
-
|
||||
path: /
|
||||
root: /home/geminirocks/data/home
|
||||
twins.rocketnine.space:
|
||||
cert: /srv/twins.rocketnine.space/data/cert.crt
|
||||
key: /srv/twins.rocketnine.space/data/cert.key
|
||||
paths:
|
||||
-
|
||||
path: /sites
|
||||
root: /home/twins/data/sites
|
||||
-
|
||||
path: /
|
||||
root: /home/twins/data/home
|
||||
root: /home/twins/public_html
|
||||
```
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
|
@ -12,8 +13,9 @@ import (
|
|||
"github.com/h2non/filetype"
|
||||
)
|
||||
|
||||
func serveDirList(c net.Conn, serve *pathConfig, request *url.URL, dirPath string) int {
|
||||
func buildDirList(request *url.URL, dirPath string) ([]byte, error) {
|
||||
var (
|
||||
b = &bytes.Buffer{}
|
||||
files []os.FileInfo
|
||||
numDirs int
|
||||
numFiles int
|
||||
|
@ -36,7 +38,7 @@ func serveDirList(c net.Conn, serve *pathConfig, request *url.URL, dirPath strin
|
|||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return writeStatus(c, statusTemporaryFailure)
|
||||
return nil, err
|
||||
}
|
||||
// List directories first
|
||||
sort.Slice(files, func(i, j int) bool {
|
||||
|
@ -48,24 +50,22 @@ func serveDirList(c net.Conn, serve *pathConfig, request *url.URL, dirPath strin
|
|||
return i < j
|
||||
})
|
||||
|
||||
writeSuccess(c, serve, geminiType, -1)
|
||||
|
||||
fmt.Fprintf(c, "# %s%s", request.Path, newLine)
|
||||
fmt.Fprintf(b, "# %s%s", request.Path, newLine)
|
||||
if numDirs == 1 {
|
||||
c.Write([]byte("1 directory"))
|
||||
b.Write([]byte("1 directory"))
|
||||
} else {
|
||||
fmt.Fprintf(c, "%d directories", numDirs)
|
||||
fmt.Fprintf(b, "%d directories", numDirs)
|
||||
}
|
||||
c.Write([]byte(", "))
|
||||
b.Write([]byte(", "))
|
||||
if numDirs == 1 {
|
||||
c.Write([]byte("1 file"))
|
||||
b.Write([]byte("1 file"))
|
||||
} else {
|
||||
fmt.Fprintf(c, "%d files", numFiles)
|
||||
fmt.Fprintf(b, "%d files", numFiles)
|
||||
}
|
||||
c.Write([]byte(newLine + newLine))
|
||||
b.Write([]byte(newLine + newLine))
|
||||
|
||||
if request.Path != "/" {
|
||||
c.Write([]byte("=> ../ ../" + newLine + newLine))
|
||||
b.Write([]byte("=> ../ ../" + newLine + newLine))
|
||||
}
|
||||
|
||||
for _, info := range files {
|
||||
|
@ -76,10 +76,10 @@ func serveDirList(c net.Conn, serve *pathConfig, request *url.URL, dirPath strin
|
|||
filePath += "/"
|
||||
}
|
||||
|
||||
c.Write([]byte("=> " + fileName + " " + filePath + newLine))
|
||||
b.Write([]byte("=> " + fileName + " " + filePath + newLine))
|
||||
|
||||
if info.IsDir() || info.Mode()&os.ModeSymlink != 0 {
|
||||
c.Write([]byte(newLine))
|
||||
b.Write([]byte(newLine))
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -87,8 +87,20 @@ func serveDirList(c net.Conn, serve *pathConfig, request *url.URL, dirPath strin
|
|||
if !info.ModTime().IsZero() {
|
||||
modified = info.ModTime().Format("2006-01-02 3:04 PM")
|
||||
}
|
||||
c.Write([]byte(modified + " - " + formatFileSize(info.Size()) + newLine + newLine))
|
||||
b.Write([]byte(modified + " - " + formatFileSize(info.Size()) + newLine + newLine))
|
||||
}
|
||||
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
func serveDirList(c net.Conn, serve *pathConfig, request *url.URL, dirPath string) int {
|
||||
dirList, err := buildDirList(request, dirPath)
|
||||
if err != nil {
|
||||
return writeStatus(c, statusTemporaryFailure)
|
||||
}
|
||||
|
||||
writeSuccess(c, serve, geminiType, -1)
|
||||
c.Write(dirList)
|
||||
return statusSuccess
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"gitlab.com/tslocum/gmitohtml/pkg/gmitohtml"
|
||||
|
@ -114,22 +115,34 @@ func serveHTTPS(w http.ResponseWriter, r *http.Request) (int, int64, string) {
|
|||
return status, -1, serve.Log
|
||||
}
|
||||
|
||||
_, err := os.Stat(path.Join(filePath, "index.gmi"))
|
||||
if err != nil {
|
||||
_, err := os.Stat(path.Join(filePath, "index.gemini"))
|
||||
if err != nil {
|
||||
if serve.List {
|
||||
var found bool
|
||||
for _, indexFile := range indexFiles {
|
||||
_, err := os.Stat(path.Join(filePath, indexFile))
|
||||
if err == nil || os.IsExist(err) {
|
||||
filePath = path.Join(filePath, indexFile)
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
if serve.List {
|
||||
dirList, err := buildDirList(r.URL, filePath)
|
||||
if err != nil {
|
||||
status := http.StatusInternalServerError
|
||||
http.Error(w, "HTTPS dir lost not yet implemented", status)
|
||||
http.Error(w, "Failed to build directory listing", status)
|
||||
return status, -1, serve.Log
|
||||
}
|
||||
result := gmitohtml.Convert([]byte(dirList), r.URL.String())
|
||||
|
||||
http.NotFound(w, r)
|
||||
return http.StatusNotFound, -1, serve.Log
|
||||
status := http.StatusOK
|
||||
w.Header().Set("Content-Type", htmlType)
|
||||
w.WriteHeader(status)
|
||||
|
||||
w.Write(result)
|
||||
return status, int64(len(result)), serve.Log
|
||||
}
|
||||
filePath = path.Join(filePath, "index.gemini")
|
||||
} else {
|
||||
filePath = path.Join(filePath, "index.gmi")
|
||||
http.NotFound(w, r)
|
||||
return http.StatusNotFound, -1, serve.Log
|
||||
}
|
||||
} else if hasTrailingSlash && len(r.URL.Path) > 1 {
|
||||
u, err := url.Parse(r.URL.String())
|
||||
|
@ -152,10 +165,20 @@ func serveHTTPS(w http.ResponseWriter, r *http.Request) (int, int64, string) {
|
|||
return status, -1, serve.Log
|
||||
}
|
||||
|
||||
result := gmitohtml.Convert([]byte(data), r.URL.String())
|
||||
var result []byte
|
||||
contentType := htmlType
|
||||
fileExt := strings.ToLower(filepath.Ext(filePath))
|
||||
if fileExt == ".gmi" || fileExt == ".gemini" {
|
||||
result = gmitohtml.Convert([]byte(data), r.URL.String())
|
||||
} else if fileExt == ".htm" || fileExt == ".html" {
|
||||
result = data
|
||||
} else {
|
||||
result = data
|
||||
contentType = plainType
|
||||
}
|
||||
|
||||
status := http.StatusOK
|
||||
w.Header().Set("Content-Type", htmlType)
|
||||
w.Header().Set("Content-Type", contentType)
|
||||
w.WriteHeader(status)
|
||||
|
||||
w.Write(result)
|
||||
|
|
43
server.go
43
server.go
|
@ -2,7 +2,6 @@ package main
|
|||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
|
@ -61,6 +60,8 @@ var newLine = "\r\n"
|
|||
|
||||
var logLock sync.Mutex
|
||||
|
||||
var indexFiles = []string{"index.gmi", "index.gemini"}
|
||||
|
||||
func writeHeader(c net.Conn, code int, meta string) int {
|
||||
fmt.Fprintf(c, "%d %s%s", code, meta, newLine)
|
||||
return code
|
||||
|
@ -110,22 +111,6 @@ func writeSuccess(c net.Conn, serve *pathConfig, contentType string, size int64)
|
|||
return statusSuccess
|
||||
}
|
||||
|
||||
func scanCRLF(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
||||
if atEOF && len(data) == 0 {
|
||||
return 0, nil, nil
|
||||
}
|
||||
if i := bytes.IndexByte(data, '\r'); i >= 0 {
|
||||
// We have a full newline-terminated line.
|
||||
return i + 1, data[0:i], nil
|
||||
}
|
||||
// If we're at EOF, we have a final, non-terminated line. Return it.
|
||||
if atEOF {
|
||||
return len(data), data, nil
|
||||
}
|
||||
// Request more data.
|
||||
return 0, nil, nil
|
||||
}
|
||||
|
||||
func replaceWithUserInput(command []string, request *url.URL) []string {
|
||||
newCommand := make([]string, len(command))
|
||||
copy(newCommand, command)
|
||||
|
@ -229,18 +214,20 @@ func servePath(c *tls.Conn, request *url.URL, serve *pathConfig) (int, int64) {
|
|||
return writeHeader(c, statusRedirectPermanent, request.String()+"/"), -1
|
||||
}
|
||||
|
||||
_, err := os.Stat(path.Join(filePath, "index.gmi"))
|
||||
if err != nil {
|
||||
_, err := os.Stat(path.Join(filePath, "index.gemini"))
|
||||
if err != nil {
|
||||
if serve.List {
|
||||
return serveDirList(c, serve, request, filePath), -1
|
||||
}
|
||||
return writeStatus(c, statusNotFound), -1
|
||||
var found bool
|
||||
for _, indexFile := range indexFiles {
|
||||
_, err := os.Stat(path.Join(filePath, indexFile))
|
||||
if err == nil || os.IsExist(err) {
|
||||
filePath = path.Join(filePath, indexFile)
|
||||
found = true
|
||||
break
|
||||
}
|
||||
filePath = path.Join(filePath, "index.gemini")
|
||||
} else {
|
||||
filePath = path.Join(filePath, "index.gmi")
|
||||
}
|
||||
if !found {
|
||||
if serve.List {
|
||||
return serveDirList(c, serve, request, filePath), -1
|
||||
}
|
||||
return writeStatus(c, statusNotFound), -1
|
||||
}
|
||||
} else if hasTrailingSlash && len(request.Path) > 1 {
|
||||
r := request.String()
|
||||
|
|
Loading…
Reference in a new issue