Merge pull request #80 from yankay/add-index

feat: Add Show Index
This commit is contained in:
Jeromy Streets 2022-08-11 08:50:33 -07:00 committed by GitHub
commit 0c18d4bd54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 105 additions and 4 deletions

View File

@ -34,8 +34,11 @@ HOST=
# If assigned, must be a valid port number. # If assigned, must be a valid port number.
PORT=8080 PORT=8080
# Automatically serve the index file for a given directory (default). If set to # When set to 'true' the index.html file in the folder will be served. And
# 'false', URLs ending with a '/' will return 'NOT FOUND'. # the file list will not be served.
ALLOW_INDEX=true
# Automatically serve the index of file list for a given directory (default).
SHOW_LISTING=true SHOW_LISTING=true
# Folder with the content to serve. # Folder with the content to serve.

View File

@ -61,6 +61,12 @@ ENVIRONMENT VARIABLES
Examples: Examples:
REFERRERS='http://localhost,https://some.site,http://other.site:8080' REFERRERS='http://localhost,https://some.site,http://other.site:8080'
REFERRERS=',http://localhost,https://some.site,http://other.site:8080' REFERRERS=',http://localhost,https://some.site,http://other.site:8080'
ALLOW_INDEX
When set to 'true' the index.html file in the folder(not include the
sub folders) will be served. And the file list will not be served.
For example, if the client requests 'http://127.0.0.1/' the 'index.html'
file in the root of the directory being served is returned. Default value
is 'true'.
SHOW_LISTING SHOW_LISTING
Automatically serve the index file for the directory if requested. For Automatically serve the index file for the directory if requested. For
example, if the client requests 'http://127.0.0.1/' the 'index.html' example, if the client requests 'http://127.0.0.1/' the 'index.html'
@ -161,12 +167,22 @@ USAGE
export FOLDER=/var/www export FOLDER=/var/www
export PORT=80 export PORT=80
export ALLOW_INDEX=true # Default behavior
export SHOW_LISTING=true # Default behavior export SHOW_LISTING=true # Default behavior
static-file-server static-file-server
Retrieve 'index.html' with: wget http://my.machine/ Retrieve 'index.html' with: wget http://my.machine/
export FOLDER=/var/www export FOLDER=/var/www
export PORT=80 export PORT=80
export ALLOW_INDEX=true # Default behavior
export SHOW_LISTING=false
static-file-server
Retrieve 'index.html' with: wget http://my.machine/
Returns 'NOT FOUND': wget http://my.machine/dir/
export FOLDER=/var/www
export PORT=80
export ALLOW_INDEX=false
export SHOW_LISTING=false export SHOW_LISTING=false
static-file-server static-file-server
Returns 'NOT FOUND': wget http://my.machine/ Returns 'NOT FOUND': wget http://my.machine/

View File

@ -59,9 +59,12 @@ func handlerSelector() (handler http.HandlerFunc) {
// Determine whether index files should hidden. // Determine whether index files should hidden.
if !config.Get.ShowListing { if !config.Get.ShowListing {
handler = handle.IgnoreIndex(handler) if config.Get.AllowIndex {
handler = handle.PreventListings(handler, config.Get.Folder, config.Get.URLPrefix)
} else {
handler = handle.IgnoreIndex(handler)
}
} }
// If configured, apply wildcard CORS support. // If configured, apply wildcard CORS support.
if config.Get.Cors { if config.Get.Cors {
handler = handle.AddCorsWildcardHeaders(handler) handler = handle.AddCorsWildcardHeaders(handler)

View File

@ -21,6 +21,7 @@ var (
Folder string `yaml:"folder"` Folder string `yaml:"folder"`
Host string `yaml:"host"` Host string `yaml:"host"`
Port uint16 `yaml:"port"` Port uint16 `yaml:"port"`
AllowIndex bool `yaml:"allow-index"`
ShowListing bool `yaml:"show-listing"` ShowListing bool `yaml:"show-listing"`
TLSCert string `yaml:"tls-cert"` TLSCert string `yaml:"tls-cert"`
TLSKey string `yaml:"tls-key"` TLSKey string `yaml:"tls-key"`
@ -39,6 +40,7 @@ const (
hostKey = "HOST" hostKey = "HOST"
portKey = "PORT" portKey = "PORT"
referrersKey = "REFERRERS" referrersKey = "REFERRERS"
allowIndexKey = "ALLOW_INDEX"
showListingKey = "SHOW_LISTING" showListingKey = "SHOW_LISTING"
tlsCertKey = "TLS_CERT" tlsCertKey = "TLS_CERT"
tlsKeyKey = "TLS_KEY" tlsKeyKey = "TLS_KEY"
@ -53,6 +55,7 @@ var (
defaultHost = "" defaultHost = ""
defaultPort = uint16(8080) defaultPort = uint16(8080)
defaultReferrers = []string{} defaultReferrers = []string{}
defaultAllowIndex = true
defaultShowListing = true defaultShowListing = true
defaultTLSCert = "" defaultTLSCert = ""
defaultTLSKey = "" defaultTLSKey = ""
@ -73,6 +76,7 @@ func setDefaults() {
Get.Host = defaultHost Get.Host = defaultHost
Get.Port = defaultPort Get.Port = defaultPort
Get.Referrers = defaultReferrers Get.Referrers = defaultReferrers
Get.AllowIndex = defaultAllowIndex
Get.ShowListing = defaultShowListing Get.ShowListing = defaultShowListing
Get.TLSCert = defaultTLSCert Get.TLSCert = defaultTLSCert
Get.TLSKey = defaultTLSKey Get.TLSKey = defaultTLSKey
@ -124,6 +128,7 @@ func overrideWithEnvVars() {
Get.Folder = envAsStr(folderKey, Get.Folder) Get.Folder = envAsStr(folderKey, Get.Folder)
Get.Host = envAsStr(hostKey, Get.Host) Get.Host = envAsStr(hostKey, Get.Host)
Get.Port = envAsUint16(portKey, Get.Port) Get.Port = envAsUint16(portKey, Get.Port)
Get.AllowIndex = envAsBool(allowIndexKey, Get.AllowIndex)
Get.ShowListing = envAsBool(showListingKey, Get.ShowListing) Get.ShowListing = envAsBool(showListingKey, Get.ShowListing)
Get.TLSCert = envAsStr(tlsCertKey, Get.TLSCert) Get.TLSCert = envAsStr(tlsCertKey, Get.TLSCert)
Get.TLSKey = envAsStr(tlsKeyKey, Get.TLSKey) Get.TLSKey = envAsStr(tlsKeyKey, Get.TLSKey)

View File

@ -82,6 +82,7 @@ func TestOverrideWithEnvvars(t *testing.T) {
testFolder := "/my/directory" testFolder := "/my/directory"
testHost := "apets.life" testHost := "apets.life"
testPort := uint16(666) testPort := uint16(666)
testAllowIndex := false
testShowListing := false testShowListing := false
testTLSCert := "my.pem" testTLSCert := "my.pem"
testTLSKey := "my.key" testTLSKey := "my.key"
@ -92,6 +93,7 @@ func TestOverrideWithEnvvars(t *testing.T) {
os.Setenv(folderKey, testFolder) os.Setenv(folderKey, testFolder)
os.Setenv(hostKey, testHost) os.Setenv(hostKey, testHost)
os.Setenv(portKey, strconv.Itoa(int(testPort))) os.Setenv(portKey, strconv.Itoa(int(testPort)))
os.Setenv(allowIndexKey, fmt.Sprintf("%t", testAllowIndex))
os.Setenv(showListingKey, fmt.Sprintf("%t", testShowListing)) os.Setenv(showListingKey, fmt.Sprintf("%t", testShowListing))
os.Setenv(tlsCertKey, testTLSCert) os.Setenv(tlsCertKey, testTLSCert)
os.Setenv(tlsKeyKey, testTLSKey) os.Setenv(tlsKeyKey, testTLSKey)

View File

@ -6,6 +6,8 @@ import (
"fmt" "fmt"
"log" "log"
"net/http" "net/http"
"os"
"path"
"strings" "strings"
) )
@ -130,6 +132,23 @@ func Prefix(serveFile FileServerFunc, folder, urlPrefix string) http.HandlerFunc
} }
} }
// PreventListings returns a function that prevents listing of directories but
// still allows index.html to be served.
func PreventListings(serve http.HandlerFunc, folder string, urlPrefix string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if strings.HasSuffix(r.URL.Path, "/") {
// If the directory does not contain an index.html file, then
// return 'NOT FOUND' to prevent listing of the directory.
stat, err := os.Stat(path.Join(folder, strings.TrimPrefix(r.URL.Path, urlPrefix), "index.html"))
if err != nil || (err == nil && !stat.Mode().IsRegular()) {
http.NotFound(w, r)
return
}
}
serve(w, r)
}
}
// IgnoreIndex wraps an HTTP request. In the event of a folder root request, // IgnoreIndex wraps an HTTP request. In the event of a folder root request,
// this function will automatically return 'NOT FOUND' as opposed to default // this function will automatically return 'NOT FOUND' as opposed to default
// behavior where the index file for that directory is retrieved. // behavior where the index file for that directory is retrieved.

View File

@ -27,6 +27,8 @@ var (
tmpSubDeepIndexName = "sub/deep/index.html" tmpSubDeepIndexName = "sub/deep/index.html"
tmpSubDeepFileName = "sub/deep/file.txt" tmpSubDeepFileName = "sub/deep/file.txt"
tmpSubDeepBadName = "sub/deep/bad.txt" tmpSubDeepBadName = "sub/deep/bad.txt"
tmpNoIndexDir = "noindex/"
tmpNoIndexName = "noindex/noindex.txt"
tmpIndex = "Space: the final frontier" tmpIndex = "Space: the final frontier"
tmpFile = "These are the voyages of the starship Enterprise." tmpFile = "These are the voyages of the starship Enterprise."
@ -48,6 +50,7 @@ var (
baseDir + tmpSubFileName: tmpSubFile, baseDir + tmpSubFileName: tmpSubFile,
baseDir + tmpSubDeepIndexName: tmpSubDeepIndex, baseDir + tmpSubDeepIndexName: tmpSubDeepIndex,
baseDir + tmpSubDeepFileName: tmpSubDeepFile, baseDir + tmpSubDeepFileName: tmpSubDeepFile,
baseDir + tmpNoIndexName: tmpSubDeepFile,
} }
serveFileFuncs = []FileServerFunc{ serveFileFuncs = []FileServerFunc{
@ -337,6 +340,56 @@ func TestIgnoreIndex(t *testing.T) {
} }
} }
func TestPreventListings(t *testing.T) {
testCases := []struct {
name string
path string
code int
contents string
}{
{"Good base dir", "", ok, tmpIndex},
{"Good base index", tmpIndexName, redirect, nothing},
{"Good base file", tmpFileName, ok, tmpFile},
{"Bad base file", tmpBadName, missing, notFound},
{"Good subdir dir", subDir, ok, tmpSubIndex},
{"Good subdir index", tmpSubIndexName, redirect, nothing},
{"Good subdir file", tmpSubFileName, ok, tmpSubFile},
{"Dir without index", tmpNoIndexDir, missing, notFound},
}
for _, serveFile := range serveFileFuncs {
handler := PreventListings(Basic(serveFile, baseDir), baseDir, "")
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
fullpath := "http://localhost/" + tc.path
req := httptest.NewRequest("GET", fullpath, nil)
w := httptest.NewRecorder()
handler(w, req)
resp := w.Result()
body, err := ioutil.ReadAll(resp.Body)
if nil != err {
t.Errorf("While reading body got %v", err)
}
contents := string(body)
if tc.code != resp.StatusCode {
t.Errorf(
"While retrieving %s expected status code of %d but got %d",
fullpath, tc.code, resp.StatusCode,
)
}
if tc.contents != contents {
t.Errorf(
"While retrieving %s expected contents '%s' but got '%s'",
fullpath, tc.contents, contents,
)
}
})
}
}
}
func TestAddAccessKey(t *testing.T) { func TestAddAccessKey(t *testing.T) {
// Prepare testing data. // Prepare testing data.
accessKey := "my-access-key" accessKey := "my-access-key"