mirror of
https://github.com/halverneus/static-file-server.git
synced 2024-11-24 09:05:30 +00:00
Added the ability to enable debug logging.
This commit is contained in:
parent
053077e1b2
commit
f2f41ef96a
@ -8,6 +8,9 @@ Available on GitHub at https://github.com/halverneus/static-file-server
|
||||
Environment variables with defaults:
|
||||
|
||||
```bash
|
||||
# Enable debugging for troubleshooting. If set to 'true' this prints extra
|
||||
# information during execution. Default value is 'false'.
|
||||
DEBUG=false
|
||||
# Optional Hostname for binding. Leave black to accept any incoming HTTP request
|
||||
# on the prescribed port.
|
||||
HOST=
|
||||
@ -34,6 +37,7 @@ configuration file with defaults. Pass in the path to the configuration file
|
||||
using the command line option ('-c', '-config', '--config').
|
||||
|
||||
```yaml
|
||||
debug: false
|
||||
host: ""
|
||||
port: 8080
|
||||
show-listing: true
|
||||
|
@ -33,6 +33,10 @@ DEPENDENCIES
|
||||
None... not even libc!
|
||||
|
||||
ENVIRONMENT VARIABLES
|
||||
DEBUG
|
||||
When set to 'true' enables additional logging, including the
|
||||
configuration used and an access log for each request. Default value is
|
||||
'false'.
|
||||
FOLDER
|
||||
The path to the folder containing the contents to be served over
|
||||
HTTP(s). If not supplied, defaults to '/web' (for Docker reasons).
|
||||
@ -69,6 +73,7 @@ CONFIGURATION FILE
|
||||
|
||||
Example config.yml with defaults:
|
||||
----------------------------------------------------------------------------
|
||||
debug: false
|
||||
folder: /web
|
||||
host: ""
|
||||
port: 8080
|
||||
|
@ -16,6 +16,9 @@ var (
|
||||
|
||||
// Run server.
|
||||
func Run() error {
|
||||
if config.Get.Debug {
|
||||
config.Log()
|
||||
}
|
||||
// Choose and set the appropriate, optimized static file serving function.
|
||||
handler := selectHandler()
|
||||
|
||||
@ -30,11 +33,21 @@ func Run() error {
|
||||
// handlerSelector returns the appropriate request handler based on
|
||||
// configuration.
|
||||
func handlerSelector() (handler http.HandlerFunc) {
|
||||
var serveFileHandler handle.FileServerFunc
|
||||
serveFileHandler = http.ServeFile
|
||||
if config.Get.Debug {
|
||||
serveFileHandler = handle.WithLogging(serveFileHandler)
|
||||
}
|
||||
|
||||
// Choose and set the appropriate, optimized static file serving function.
|
||||
if 0 == len(config.Get.URLPrefix) {
|
||||
handler = handle.Basic(config.Get.Folder)
|
||||
handler = handle.Basic(serveFileHandler, config.Get.Folder)
|
||||
} else {
|
||||
handler = handle.Prefix(config.Get.Folder, config.Get.URLPrefix)
|
||||
handler = handle.Prefix(
|
||||
serveFileHandler,
|
||||
config.Get.Folder,
|
||||
config.Get.URLPrefix,
|
||||
)
|
||||
}
|
||||
|
||||
// Determine whether index files should hidden.
|
||||
|
@ -17,8 +17,14 @@ func TestRun(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
config.Get.Debug = false
|
||||
if err := Run(); listenerError != err {
|
||||
t.Errorf("Expected %v but got %v", listenerError, err)
|
||||
t.Errorf("Without debug expected %v but got %v", listenerError, err)
|
||||
}
|
||||
|
||||
config.Get.Debug = true
|
||||
if err := Run(); listenerError != err {
|
||||
t.Errorf("With debug expected %v but got %v", listenerError, err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,18 +38,24 @@ func TestHandlerSelector(t *testing.T) {
|
||||
folder string
|
||||
prefix string
|
||||
listing bool
|
||||
debug bool
|
||||
}{
|
||||
{"Basic handler", testFolder, "", true},
|
||||
{"Prefix handler", testFolder, testPrefix, true},
|
||||
{"Basic and hide listing handler", testFolder, "", false},
|
||||
{"Prefix and hide listing handler", testFolder, testPrefix, false},
|
||||
{"Basic handler w/o debug", testFolder, "", true, false},
|
||||
{"Prefix handler w/o debug", testFolder, testPrefix, true, false},
|
||||
{"Basic and hide listing handler w/o debug", testFolder, "", false, false},
|
||||
{"Prefix and hide listing handler w/o debug", testFolder, testPrefix, false, false},
|
||||
{"Basic handler w/debug", testFolder, "", true, true},
|
||||
{"Prefix handler w/debug", testFolder, testPrefix, true, true},
|
||||
{"Basic and hide listing handler w/debug", testFolder, "", false, true},
|
||||
{"Prefix and hide listing handler w/debug", testFolder, testPrefix, false, true},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
config.Get.Debug = tc.debug
|
||||
config.Get.Folder = tc.folder
|
||||
config.Get.URLPrefix = tc.prefix
|
||||
config.Get.ShowListing = tc.listing
|
||||
config.Get.URLPrefix = tc.prefix
|
||||
|
||||
handlerSelector()
|
||||
})
|
||||
|
@ -16,10 +16,10 @@ var (
|
||||
MajorVersion = 1
|
||||
|
||||
// MinorVersion of static-file-server.
|
||||
MinorVersion = 3
|
||||
MinorVersion = 4
|
||||
|
||||
// FixVersion of static-file-server.
|
||||
FixVersion = 3
|
||||
FixVersion = 0
|
||||
|
||||
// Text for directly accessing the static-file-server version.
|
||||
Text = fmt.Sprintf(
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
var (
|
||||
// Get the desired configuration value.
|
||||
Get struct {
|
||||
Debug bool `yaml:"debug"`
|
||||
Folder string `yaml:"folder"`
|
||||
Host string `yaml:"host"`
|
||||
Port uint16 `yaml:"port"`
|
||||
@ -25,6 +26,7 @@ var (
|
||||
)
|
||||
|
||||
const (
|
||||
debugKey = "DEBUG"
|
||||
folderKey = "FOLDER"
|
||||
hostKey = "HOST"
|
||||
portKey = "PORT"
|
||||
@ -35,6 +37,7 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
defaultDebug = false
|
||||
defaultFolder = "/web"
|
||||
defaultHost = ""
|
||||
defaultPort = uint16(8080)
|
||||
@ -50,6 +53,7 @@ func init() {
|
||||
}
|
||||
|
||||
func setDefaults() {
|
||||
Get.Debug = defaultDebug
|
||||
Get.Folder = defaultFolder
|
||||
Get.Host = defaultHost
|
||||
Get.Port = defaultPort
|
||||
@ -82,9 +86,21 @@ func Load(filename string) (err error) {
|
||||
return validate()
|
||||
}
|
||||
|
||||
// Log the current configuration.
|
||||
func Log() {
|
||||
// YAML marshalling should never error, but if it could, the result is that
|
||||
// the contents of the configuration are not logged.
|
||||
contents, _ := yaml.Marshal(&Get)
|
||||
|
||||
// Log the configuration.
|
||||
fmt.Println("Using the following configuration:")
|
||||
fmt.Println(string(contents))
|
||||
}
|
||||
|
||||
// overrideWithEnvVars the default values and the configuration file values.
|
||||
func overrideWithEnvVars() {
|
||||
// Assign envvars, if set.
|
||||
Get.Debug = envAsBool(debugKey, Get.Debug)
|
||||
Get.Folder = envAsStr(folderKey, Get.Folder)
|
||||
Get.Host = envAsStr(hostKey, Get.Host)
|
||||
Get.Port = envAsUint16(portKey, Get.Port)
|
||||
|
@ -6,6 +6,8 @@ import (
|
||||
"os"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
func TestLoad(t *testing.T) {
|
||||
@ -65,8 +67,17 @@ func TestLoad(t *testing.T) {
|
||||
}(t)
|
||||
}
|
||||
|
||||
func TestLog(t *testing.T) {
|
||||
// Test whether YAML marshalling works, as that is the only error case.
|
||||
if _, err := yaml.Marshal(&Get); nil != err {
|
||||
t.Errorf("While testing YAML marshalling for config Log() got %v", err)
|
||||
}
|
||||
Log()
|
||||
}
|
||||
|
||||
func TestOverrideWithEnvvars(t *testing.T) {
|
||||
// Choose values that are different than defaults.
|
||||
testDebug := true
|
||||
testFolder := "/my/directory"
|
||||
testHost := "apets.life"
|
||||
testPort := uint16(666)
|
||||
@ -76,6 +87,7 @@ func TestOverrideWithEnvvars(t *testing.T) {
|
||||
testURLPrefix := "/url/prefix"
|
||||
|
||||
// Set all environment variables with test values.
|
||||
os.Setenv(debugKey, fmt.Sprintf("%t", testDebug))
|
||||
os.Setenv(folderKey, testFolder)
|
||||
os.Setenv(hostKey, testHost)
|
||||
os.Setenv(portKey, strconv.Itoa(int(testPort)))
|
||||
@ -113,6 +125,7 @@ func TestOverrideWithEnvvars(t *testing.T) {
|
||||
// Verify defaults.
|
||||
setDefaults()
|
||||
phase := "defaults"
|
||||
equalBool(t, phase, debugKey, defaultDebug, Get.Debug)
|
||||
equalStrings(t, phase, folderKey, defaultFolder, Get.Folder)
|
||||
equalStrings(t, phase, hostKey, defaultHost, Get.Host)
|
||||
equalUint16(t, phase, portKey, defaultPort, Get.Port)
|
||||
@ -126,6 +139,7 @@ func TestOverrideWithEnvvars(t *testing.T) {
|
||||
|
||||
// Verify overrides.
|
||||
phase = "overrides"
|
||||
equalBool(t, phase, debugKey, testDebug, Get.Debug)
|
||||
equalStrings(t, phase, folderKey, testFolder, Get.Folder)
|
||||
equalStrings(t, phase, hostKey, testHost, Get.Host)
|
||||
equalUint16(t, phase, portKey, testPort, Get.Port)
|
||||
|
@ -1,6 +1,7 @@
|
||||
package handle
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
@ -21,23 +22,43 @@ var (
|
||||
// occur.
|
||||
type ListenerFunc func(string, http.HandlerFunc) error
|
||||
|
||||
// FileServerFunc is used to serve the file from the local file system to the
|
||||
// requesting client.
|
||||
type FileServerFunc func(http.ResponseWriter, *http.Request, string)
|
||||
|
||||
// WithLogging returns a function that logs information about the request prior
|
||||
// to serving the requested file.
|
||||
func WithLogging(serveFile FileServerFunc) FileServerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request, name string) {
|
||||
log.Printf(
|
||||
"REQ: %s %s %s%s -> %s\n",
|
||||
r.Method,
|
||||
r.Proto,
|
||||
r.Host,
|
||||
r.URL.Path,
|
||||
name,
|
||||
)
|
||||
serveFile(w, r, name)
|
||||
}
|
||||
}
|
||||
|
||||
// Basic file handler servers files from the passed folder.
|
||||
func Basic(folder string) http.HandlerFunc {
|
||||
func Basic(serveFile FileServerFunc, folder string) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
http.ServeFile(w, r, folder+r.URL.Path)
|
||||
serveFile(w, r, folder+r.URL.Path)
|
||||
}
|
||||
}
|
||||
|
||||
// Prefix file handler is an alternative to Basic where a URL prefix is removed
|
||||
// prior to serving a file (http://my.machine/prefix/file.txt will serve
|
||||
// file.txt from the root of the folder being served (ignoring 'prefix')).
|
||||
func Prefix(folder, urlPrefix string) http.HandlerFunc {
|
||||
func Prefix(serveFile FileServerFunc, folder, urlPrefix string) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if !strings.HasPrefix(r.URL.Path, urlPrefix) {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
http.ServeFile(w, r, folder+strings.TrimPrefix(r.URL.Path, urlPrefix))
|
||||
serveFile(w, r, folder+strings.TrimPrefix(r.URL.Path, urlPrefix))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,6 +46,11 @@ var (
|
||||
baseDir + tmpSubDeepIndexName: tmpSubDeepIndex,
|
||||
baseDir + tmpSubDeepFileName: tmpSubDeepFile,
|
||||
}
|
||||
|
||||
serveFileFuncs = []FileServerFunc{
|
||||
http.ServeFile,
|
||||
WithLogging(http.ServeFile),
|
||||
}
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
@ -79,7 +84,7 @@ func teardown() (err error) {
|
||||
return os.RemoveAll("tmp")
|
||||
}
|
||||
|
||||
func TestBasic(t *testing.T) {
|
||||
func TestBasicWithAndWithoutLogging(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
path string
|
||||
@ -95,34 +100,36 @@ func TestBasic(t *testing.T) {
|
||||
{"Good subdir file", tmpSubFileName, ok, tmpSubFile},
|
||||
}
|
||||
|
||||
handler := Basic(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()
|
||||
for _, serveFile := range serveFileFuncs {
|
||||
handler := Basic(serveFile, 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)
|
||||
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,
|
||||
)
|
||||
}
|
||||
})
|
||||
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,
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -145,34 +152,36 @@ func TestPrefix(t *testing.T) {
|
||||
{"Unknown prefix", tmpFileName, missing, notFound},
|
||||
}
|
||||
|
||||
handler := Prefix(baseDir, prefix)
|
||||
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()
|
||||
for _, serveFile := range serveFileFuncs {
|
||||
handler := Prefix(serveFile, baseDir, prefix)
|
||||
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)
|
||||
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,
|
||||
)
|
||||
}
|
||||
})
|
||||
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,
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -192,64 +201,39 @@ func TestIgnoreIndex(t *testing.T) {
|
||||
{"Good subdir file", tmpSubFileName, ok, tmpSubFile},
|
||||
}
|
||||
|
||||
handler := IgnoreIndex(Basic(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()
|
||||
for _, serveFile := range serveFileFuncs {
|
||||
handler := IgnoreIndex(Basic(serveFile, 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)
|
||||
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,
|
||||
)
|
||||
}
|
||||
})
|
||||
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 TestIgnoreIndex(t *testing.T) {
|
||||
// handler := IgnoreIndex(Basic("tmp"))
|
||||
// testCases := []struct {
|
||||
// name string
|
||||
// path string
|
||||
// code int
|
||||
// contents string
|
||||
// }{}
|
||||
|
||||
// // Build test cases for directories.
|
||||
// var dirs []string
|
||||
// for filename, contents := range files {
|
||||
// dir := path.Dir(filename)
|
||||
// found := false
|
||||
// for _, other := range dirs {
|
||||
// if other == dir {
|
||||
// found = true
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// if !found {
|
||||
// dirs = append(dirs, dir)
|
||||
// }
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
func TestListening(t *testing.T) {
|
||||
// Choose values for testing.
|
||||
called := false
|
||||
|
Loading…
Reference in New Issue
Block a user