caddyhttp: Add plaintext response to file_server browse (#6093)

* Added plaintext support to file_server browser

This commit is twofold: First it adds a new optional
field, `return_type`, to `browser` for setting the
default format of the returned index (html, json or plaintext).
This is used when the `Accept` header is set to `/*`.

Second, it adds a preliminary `text/plain`
support to the `file_server` browser that
returns a text representation of the file
system, when an `Accept: text/plain` header
is present, with the behavior discussed above.

* Added more details and better formatting to plaintext browser

* Replaced returnType conditions with a switch statement

* Simplify

---------

Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
This commit is contained in:
kylosus 2024-04-01 21:12:40 +03:00 committed by GitHub
parent 1217449609
commit 45132c5b24
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 40 additions and 8 deletions

View file

@ -28,6 +28,7 @@ import (
"path" "path"
"strings" "strings"
"sync" "sync"
"text/tabwriter"
"text/template" "text/template"
"go.uber.org/zap" "go.uber.org/zap"
@ -111,13 +112,42 @@ func (fsrv *FileServer) serveBrowse(fileSystem fs.FS, root, dirPath string, w ht
acceptHeader := strings.ToLower(strings.Join(r.Header["Accept"], ",")) acceptHeader := strings.ToLower(strings.Join(r.Header["Accept"], ","))
// write response as either JSON or HTML switch {
if strings.Contains(acceptHeader, "application/json") { case strings.Contains(acceptHeader, "application/json"):
if err := json.NewEncoder(buf).Encode(listing.Items); err != nil { if err := json.NewEncoder(buf).Encode(listing.Items); err != nil {
return caddyhttp.Error(http.StatusInternalServerError, err) return caddyhttp.Error(http.StatusInternalServerError, err)
} }
w.Header().Set("Content-Type", "application/json; charset=utf-8") w.Header().Set("Content-Type", "application/json; charset=utf-8")
} else {
case strings.Contains(acceptHeader, "text/plain"):
writer := tabwriter.NewWriter(buf, 0, 8, 1, '\t', tabwriter.AlignRight)
// Header on top
if _, err := fmt.Fprintln(writer, "Name\tSize\tModified"); err != nil {
return caddyhttp.Error(http.StatusInternalServerError, err)
}
// Lines to separate the header
if _, err := fmt.Fprintln(writer, "----\t----\t--------"); err != nil {
return caddyhttp.Error(http.StatusInternalServerError, err)
}
// Actual files
for _, item := range listing.Items {
if _, err := fmt.Fprintf(writer, "%s\t%s\t%s\n",
item.Name, item.HumanSize(), item.HumanModTime("January 2, 2006 at 15:04:05"),
); err != nil {
return caddyhttp.Error(http.StatusInternalServerError, err)
}
}
if err := writer.Flush(); err != nil {
return caddyhttp.Error(http.StatusInternalServerError, err)
}
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
default:
var fs http.FileSystem var fs http.FileSystem
if fsrv.Root != "" { if fsrv.Root != "" {
fs = http.Dir(repl.ReplaceAll(fsrv.Root, ".")) fs = http.Dir(repl.ReplaceAll(fsrv.Root, "."))

View file

@ -113,13 +113,15 @@ func (fsrv *FileServer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
fsrv.Browse = new(Browse) fsrv.Browse = new(Browse)
d.Args(&fsrv.Browse.TemplateFile) d.Args(&fsrv.Browse.TemplateFile)
for nesting := d.Nesting(); d.NextBlock(nesting); { for nesting := d.Nesting(); d.NextBlock(nesting); {
if d.Val() != "reveal_symlinks" { switch d.Val() {
return d.Errf("unknown subdirective '%s'", d.Val()) case "reveal_symlinks":
}
if fsrv.Browse.RevealSymlinks { if fsrv.Browse.RevealSymlinks {
return d.Err("Symlinks path reveal is already enabled") return d.Err("Symlinks path reveal is already enabled")
} }
fsrv.Browse.RevealSymlinks = true fsrv.Browse.RevealSymlinks = true
default:
return d.Errf("unknown subdirective '%s'", d.Val())
}
} }
case "precompressed": case "precompressed":