Skip to content

Commit

Permalink
caddyhttp: Add plaintext response to file_server browse (#6093)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
kylosus and mholt committed Apr 1, 2024
1 parent 1217449 commit 45132c5
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 8 deletions.
36 changes: 33 additions & 3 deletions modules/caddyhttp/fileserver/browse.go
Expand Up @@ -28,6 +28,7 @@ import (
"path"
"strings"
"sync"
"text/tabwriter"
"text/template"

"go.uber.org/zap"
Expand Down Expand Up @@ -111,13 +112,42 @@ func (fsrv *FileServer) serveBrowse(fileSystem fs.FS, root, dirPath string, w ht

acceptHeader := strings.ToLower(strings.Join(r.Header["Accept"], ","))

// write response as either JSON or HTML
if strings.Contains(acceptHeader, "application/json") {
switch {
case strings.Contains(acceptHeader, "application/json"):
if err := json.NewEncoder(buf).Encode(listing.Items); err != nil {
return caddyhttp.Error(http.StatusInternalServerError, err)
}
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
if fsrv.Root != "" {
fs = http.Dir(repl.ReplaceAll(fsrv.Root, "."))
Expand Down
12 changes: 7 additions & 5 deletions modules/caddyhttp/fileserver/caddyfile.go
Expand Up @@ -113,13 +113,15 @@ func (fsrv *FileServer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
fsrv.Browse = new(Browse)
d.Args(&fsrv.Browse.TemplateFile)
for nesting := d.Nesting(); d.NextBlock(nesting); {
if d.Val() != "reveal_symlinks" {
switch d.Val() {
case "reveal_symlinks":
if fsrv.Browse.RevealSymlinks {
return d.Err("Symlinks path reveal is already enabled")
}
fsrv.Browse.RevealSymlinks = true
default:
return d.Errf("unknown subdirective '%s'", d.Val())
}
if fsrv.Browse.RevealSymlinks {
return d.Err("Symlinks path reveal is already enabled")
}
fsrv.Browse.RevealSymlinks = true
}

case "precompressed":
Expand Down

0 comments on commit 45132c5

Please sign in to comment.