diff --git a/README.md b/README.md index 5aa334f..871d512 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,9 @@ Environment variables with defaults: HOST= # If assigned, must be a valid port number. PORT=8080 +# Automatically serve the index file for a given directory (default). If set to +# 'false', URLs ending with a '/' will return 'NOT FOUND'. +SHOW_LISTING=true # Folder with the content to serve. FOLDER=/web # URL path prefix. If 'my.file' is in the root of $FOLDER and $URL_PREFIX is diff --git a/serve.go b/serve.go index 5a7cca3..dc74f4d 100644 --- a/serve.go +++ b/serve.go @@ -39,6 +39,12 @@ ENVIRONMENT VARIABLES to a client without regard for the hostname. PORT The port used for binding. If not supplied, defaults to port '8080'. + SHOW_LISTING + Automatically serve the index file for the directory if requested. 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. If the value + is set to 'false', the same request will return a 'NOT FOUND'. Default + value is 'true'. TLS_CERT Path to the TLS certificate file to serve files using HTTPS. If supplied then TLS_KEY must also be supplied. If not supplied, contents will be @@ -55,6 +61,7 @@ ENVIRONMENT VARIABLES USAGE FILE LAYOUT /var/www/sub/my.file + /var/www/index.html COMMAND export FOLDER=/var/www/sub @@ -87,6 +94,18 @@ USAGE export TLS_KEY=/etc/server/my.machine.key static-file-server Retrieve with: wget https://my.machine/my.file + + export FOLDER=/var/www + export PORT=80 + export SHOW_LISTING=true # Default behavior + static-file-server + Retrieve 'index.html' with: wget http://my.machine/ + + export FOLDER=/var/www + export PORT=80 + export SHOW_LISTING=false + static-file-server + Returns 'NOT FOUND': wget http://my.machine/ ` ) @@ -110,6 +129,7 @@ func main() { folder := env("FOLDER", "/web") + "/" host := env("HOST", "") port := env("PORT", "8080") + showListing := envAsBool("SHOW_LISTING", true) tlsCert := env("TLS_CERT", "") tlsKey := env("TLS_KEY", "") urlPrefix := env("URL_PREFIX", "") @@ -137,9 +157,9 @@ func main() { // Choose and set the appropriate, optimized static file serving function. var handler http.HandlerFunc if 0 == len(urlPrefix) { - handler = basicHandler(folder) + handler = handleListing(showListing, basicHandler(folder)) } else { - handler = prefixHandler(folder, urlPrefix) + handler = handleListing(showListing, prefixHandler(folder, urlPrefix)) } http.HandleFunc("/", handler) @@ -151,6 +171,19 @@ func main() { } } +// handleListing wraps an HTTP request. In the event of a folder root request, +// setting 'show' to false will automatically return 'NOT FOUND' whereas true +// will attempt to retrieve the index file of that directory. +func handleListing(show bool, serve http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + if show || strings.HasSuffix(r.URL.Path, "/") { + http.NotFound(w, r) + return + } + serve(w, r) + } +} + // basicHandler serves files from the folder passed. func basicHandler(folder string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { @@ -178,3 +211,35 @@ func env(key, fallback string) string { } return fallback } + +// strAsBool converts the intent of the passed value into a boolean +// representation. +func strAsBool(value string) (result bool, err error) { + lvalue := strings.ToLower(value) + switch lvalue { + case "0", "false", "f", "no", "n": + result = false + case "1", "true", "t", "yes", "y": + result = true + default: + result = false + msg := "Unknown conversion from string to bool for value '%s'" + err = fmt.Errorf(msg, value) + } + return +} + +// envAsBool returns the value for an environment variable or, if not set, a +// fallback value as a boolean. +func envAsBool(key string, fallback bool) bool { + value := env(key, fmt.Sprintf("%t", fallback)) + result, err := strAsBool(value) + if nil != err { + log.Printf( + "Invalid value for '%s': %v\nUsing fallback: %t", + key, err, fallback, + ) + return fallback + } + return result +}