fileserver and log middleware as packages
This commit is contained in:
parent
e776588e5c
commit
2e196465b6
2 changed files with 148 additions and 0 deletions
115
fileserver/fileserver.go
Normal file
115
fileserver/fileserver.go
Normal file
|
@ -0,0 +1,115 @@
|
|||
package fileserver
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"mime"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/n0x1m/gmifs/gemini"
|
||||
)
|
||||
|
||||
var errDirWithoutIndexFile = errors.New("path without index.gmi not allowed")
|
||||
|
||||
func Serve(root string, autoindex bool) func(w io.Writer, r *gemini.Request) {
|
||||
return func(w io.Writer, r *gemini.Request) {
|
||||
fullpath, err := fullPath(root, r.URL.Path)
|
||||
if err != nil {
|
||||
if err == errDirWithoutIndexFile && autoindex {
|
||||
body, mimeType, err := listDirectory(fullpath, r.URL.Path)
|
||||
if err != nil {
|
||||
gemini.WriteHeader(w, gemini.StatusNotFound, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
gemini.WriteHeader(w, gemini.StatusSuccess, mimeType)
|
||||
gemini.Write(w, body)
|
||||
return
|
||||
}
|
||||
gemini.WriteHeader(w, gemini.StatusNotFound, err.Error())
|
||||
return
|
||||
}
|
||||
body, mimeType, err := readFile(fullpath)
|
||||
if err != nil {
|
||||
gemini.WriteHeader(w, gemini.StatusNotFound, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
gemini.WriteHeader(w, gemini.StatusSuccess, mimeType)
|
||||
gemini.Write(w, body)
|
||||
}
|
||||
}
|
||||
|
||||
func fullPath(root, requestPath string) (string, error) {
|
||||
fullpath := path.Join(root, requestPath)
|
||||
|
||||
pathInfo, err := os.Stat(fullpath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("path: %w", err)
|
||||
}
|
||||
|
||||
if pathInfo.IsDir() {
|
||||
subDirIndex := path.Join(fullpath, gemini.IndexFile)
|
||||
if _, err := os.Stat(subDirIndex); os.IsNotExist(err) {
|
||||
return fullpath, errDirWithoutIndexFile
|
||||
}
|
||||
|
||||
fullpath = subDirIndex
|
||||
}
|
||||
|
||||
return fullpath, nil
|
||||
}
|
||||
|
||||
func readFile(filepath string) ([]byte, string, error) {
|
||||
mimeType := getMimeType(filepath)
|
||||
if mimeType == "" {
|
||||
return nil, "", errors.New("disabled/unsupported file type")
|
||||
}
|
||||
|
||||
file, err := os.Open(filepath)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("file: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
data, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("read: %w", err)
|
||||
}
|
||||
return data, mimeType, nil
|
||||
}
|
||||
|
||||
func getMimeType(fullpath string) string {
|
||||
if ext := path.Ext(fullpath); ext != ".gmi" {
|
||||
return mime.TypeByExtension(ext)
|
||||
}
|
||||
return gemini.MimeType
|
||||
}
|
||||
|
||||
func listDirectory(fullpath, relpath string) ([]byte, string, error) {
|
||||
files, err := ioutil.ReadDir(fullpath)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
var out []byte
|
||||
parent := filepath.Dir(relpath)
|
||||
if relpath != "/" {
|
||||
out = append(out, []byte(fmt.Sprintf("Index of %s/\n\n", relpath))...)
|
||||
out = append(out, []byte(fmt.Sprintf("=> %s ..\n", parent))...)
|
||||
} else {
|
||||
out = append(out, []byte(fmt.Sprintf("Index of %s\n\n", relpath))...)
|
||||
}
|
||||
for _, f := range files {
|
||||
if relpath == "/" {
|
||||
out = append(out, []byte(fmt.Sprintf("=> %s\n", f.Name()))...)
|
||||
} else {
|
||||
out = append(out, []byte(fmt.Sprintf("=> %s/%s %s\n", relpath, f.Name(), f.Name()))...)
|
||||
}
|
||||
}
|
||||
|
||||
return out, gemini.MimeType, nil
|
||||
}
|
33
middleware/logger.go
Normal file
33
middleware/logger.go
Normal file
|
@ -0,0 +1,33 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/n0x1m/gmifs/gemini"
|
||||
)
|
||||
|
||||
func Logger(log *log.Logger) func(next gemini.Handler) gemini.Handler {
|
||||
return func(next gemini.Handler) gemini.Handler {
|
||||
fn := func(w io.Writer, r *gemini.Request) {
|
||||
t := time.Now()
|
||||
|
||||
next.ServeGemini(w, r)
|
||||
|
||||
ip := strings.Split(r.RemoteAddr, ":")[0]
|
||||
hostname, _ := os.Hostname()
|
||||
fmt.Fprintf(log.Writer(), "%s %s - - [%s] \"%s\" - %v\n",
|
||||
hostname,
|
||||
ip,
|
||||
t.Format("02/Jan/2006:15:04:05 -0700"),
|
||||
r.URL.Path,
|
||||
time.Since(t),
|
||||
)
|
||||
}
|
||||
return gemini.HandlerFunc(fn)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue