Compare commits

..

5 Commits

6 changed files with 123 additions and 99 deletions

View File

@ -127,10 +127,12 @@ Flags:
-h, --help help for roulette
--images enable support for image files
-i, --info expose informational endpoints
--maximum-files uint skip directories with file counts above this value (default 4294967295)
--minimum-files uint skip directories with file counts below this value (default 1)
--page-length uint32 pagination length for info pages
-p, --port uint16 port to listen on (default 8080)
--max-directory-scans int number of directories to scan at once (default 32)
--max-file-count int skip directories with file counts above this value (default 2147483647)
--max-file-scans int number of files to scan at once (default 256)
--min-file-count int skip directories with file counts below this value (default 1)
--page-length int pagination length for info pages
-p, --port int port to listen on (default 8080)
--prefix string root path for http handlers (for reverse proxying) (default "/")
--profile register net/http/pprof handlers
-r, --recursive recurse into subdirectories

View File

@ -16,7 +16,10 @@ import (
)
var (
ErrIncorrectRefreshInterval = errors.New("refresh interval must be a duration string >= 500ms")
ErrInvalidFileCountRange = errors.New("maximum file count limit must be greater than or equal to minimum file count limit")
ErrInvalidFileCountValue = errors.New("file count limits must be positive integers no greater than 2147483647")
ErrInvalidPort = errors.New("listen port must be an integer between 1 and 65535 inclusive")
ErrInvalidScanCount = errors.New("maximum scan count must be a positive integer no greater than 2147483647")
ErrNoMediaFound = errors.New("no supported media formats found which match all criteria")
)
@ -36,7 +39,7 @@ func notFound(w http.ResponseWriter, r *http.Request, path string) error {
startTime := time.Now()
if Verbose {
fmt.Printf("%s | Error: Unavailable file %s requested by %s\n",
fmt.Printf("%s | ERROR: Unavailable file %s requested by %s\n",
startTime.Format(logDate),
path,
r.RemoteAddr,
@ -58,17 +61,16 @@ func serverError(w http.ResponseWriter, r *http.Request, i interface{}) {
startTime := time.Now()
if Verbose {
fmt.Printf("%s | Error: Invalid request for %s from %s\n",
fmt.Printf("%s | ERROR: Invalid request for %s from %s\n",
startTime.Format(logDate),
r.URL.Path,
r.RemoteAddr,
)
}
w.WriteHeader(http.StatusInternalServerError)
w.Header().Add("Content-Type", "text/html")
io.WriteString(w, gohtml.Format(newErrorPage("Server Error", "500 Internal Server Error")))
io.WriteString(w, gohtml.Format(newErrorPage("Server Error", "An error has occurred. Please try again.")))
}
func serverErrorHandler() func(http.ResponseWriter, *http.Request, interface{}) {

View File

@ -21,14 +21,6 @@ import (
"seedno.de/seednode/roulette/types"
)
type maxConcurrency int
const (
// avoid hitting default open file descriptor limits (1024)
maxDirectoryScans maxConcurrency = 32
maxFileScans maxConcurrency = 256
)
type regexes struct {
alphanumeric *regexp.Regexp
filename *regexp.Regexp
@ -224,7 +216,7 @@ func pathIsValid(path string, paths []string) bool {
switch {
case Verbose && !matchesPrefix:
fmt.Printf("%s | Error: File outside specified path(s): %s\n",
fmt.Printf("%s | ERROR: File outside specified path(s): %s\n",
time.Now().Format(logDate),
path,
)
@ -331,7 +323,7 @@ func walkPath(path string, fileChannel chan<- string, fileScans chan int, stats
errorChannel <- err
}
if files > 0 && (files < int(MinimumFileCount)) || (files > int(MaximumFileCount)) {
if files > 0 && (files < MinFileCount) || (files > MaxFileCount) {
// This count will not otherwise include the parent directory itself, so increment by one
stats.directoriesSkipped <- directories + 1
stats.filesSkipped <- files
@ -368,8 +360,8 @@ func scanPaths(paths []string, sort string, cache *fileCache, formats *types.Typ
fileChannel := make(chan string)
errorChannel := make(chan error)
directoryScans := make(chan int, maxDirectoryScans)
fileScans := make(chan int, maxFileScans)
directoryScans := make(chan int, MaxDirScans)
fileScans := make(chan int, MaxFileScans)
done := make(chan bool, 1)
stats := &scanStats{
@ -440,7 +432,7 @@ Poll:
}
if Verbose {
fmt.Printf("%s | Index: %d/%d files across %d/%d directories in %s\n",
fmt.Printf("%s | INDEX: %d/%d files across %d/%d directories in %s\n",
time.Now().Format(logDate),
stats.filesMatched,
stats.filesMatched+stats.filesSkipped,

View File

@ -36,8 +36,8 @@ func serveIndexHtml(args []string, cache *fileCache, paginate bool) httprouter.H
startIndex = 0
stopIndex = fileCount
} else {
startIndex = ((page - 1) * int(PageLength))
stopIndex = (startIndex + int(PageLength))
startIndex = ((page - 1) * PageLength)
stopIndex = (startIndex + PageLength)
}
if startIndex > (fileCount - 1) {
@ -72,10 +72,10 @@ func serveIndexHtml(args []string, cache *fileCache, paginate bool) httprouter.H
var firstPage int = 1
var lastPage int
if fileCount%int(PageLength) == 0 {
lastPage = fileCount / int(PageLength)
if fileCount%PageLength == 0 {
lastPage = fileCount / PageLength
} else {
lastPage = (fileCount / int(PageLength)) + 1
lastPage = (fileCount / PageLength) + 1
}
if paginate {
@ -96,7 +96,7 @@ func serveIndexHtml(args []string, cache *fileCache, paginate bool) httprouter.H
nextPage := page + 1
if nextPage > lastPage {
nextPage = fileCount / int(PageLength)
nextPage = fileCount / PageLength
}
htmlBody.WriteString(fmt.Sprintf("<button onclick=\"window.location.href = '/html/%d';\">First</button>",
@ -154,8 +154,8 @@ func serveIndexJson(args []string, index *fileCache, errorChannel chan<- error)
startIndex = 0
stopIndex = fileCount
} else {
startIndex = ((page - 1) * int(PageLength))
stopIndex = (startIndex + int(PageLength))
startIndex = ((page - 1) * PageLength)
stopIndex = (startIndex + PageLength)
}
if startIndex > (fileCount - 1) {
@ -178,7 +178,7 @@ func serveIndexJson(args []string, index *fileCache, errorChannel chan<- error)
w.Write(response)
if Verbose {
fmt.Printf("%s | Serve: JSON index page (%s) to %s in %s\n",
fmt.Printf("%s | SERVE: JSON index page (%s) to %s in %s\n",
startTime.Format(logDate),
humanReadableSize(len(response)),
realIP(r),
@ -199,7 +199,7 @@ func serveAvailableExtensions() httprouter.Handle {
w.Write(response)
if Verbose {
fmt.Printf("%s | Serve: Available extension list (%s) to %s in %s\n",
fmt.Printf("%s | SERVE: Available extension list (%s) to %s in %s\n",
startTime.Format(logDate),
humanReadableSize(len(response)),
realIP(r),
@ -220,7 +220,7 @@ func serveEnabledExtensions(formats *types.Types) httprouter.Handle {
w.Write(response)
if Verbose {
fmt.Printf("%s | Serve: Registered extension list (%s) to %s in %s\n",
fmt.Printf("%s | SERVE: Registered extension list (%s) to %s in %s\n",
startTime.Format(logDate),
humanReadableSize(len(response)),
realIP(r),
@ -241,7 +241,7 @@ func serveAvailableMimeTypes() httprouter.Handle {
w.Write(response)
if Verbose {
fmt.Printf("%s | Served available MIME type list (%s) to %s in %s\n",
fmt.Printf("%s | SERVE: Available MIME type list (%s) to %s in %s\n",
startTime.Format(logDate),
humanReadableSize(len(response)),
realIP(r),
@ -262,7 +262,7 @@ func serveEnabledMimeTypes(formats *types.Types) httprouter.Handle {
w.Write(response)
if Verbose {
fmt.Printf("%s | Served registered MIME type list (%s) to %s in %s\n",
fmt.Printf("%s | SERVE: Registered MIME type list (%s) to %s in %s\n",
startTime.Format(logDate),
humanReadableSize(len(response)),
realIP(r),

View File

@ -6,12 +6,13 @@ package cmd
import (
"log"
"math"
"github.com/spf13/cobra"
)
const (
ReleaseVersion string = "0.92.2"
ReleaseVersion string = "0.94.0"
)
var (
@ -29,10 +30,12 @@ var (
Handlers bool
Images bool
Info bool
MaximumFileCount uint
MinimumFileCount uint
PageLength uint32
Port uint16
MaxDirScans int
MaxFileScans int
MaxFileCount int
MinFileCount int
PageLength int
Port int
Prefix string
Profile bool
Recursive bool
@ -48,6 +51,20 @@ var (
Use: "roulette <path> [path]...",
Short: "Serves random media from the specified directories.",
Args: cobra.MinimumNArgs(1),
PreRunE: func(cmd *cobra.Command, args []string) error {
switch {
case MaxDirScans < 1 || MaxFileScans < 1 || MaxDirScans > math.MaxInt32 || MaxFileScans > math.MaxInt32:
return ErrInvalidScanCount
case MaxFileCount < 1 || MinFileCount < 1 || MaxFileCount > math.MaxInt32 || MinFileCount > math.MaxInt32:
return ErrInvalidFileCountValue
case MinFileCount > MaxFileCount:
return ErrInvalidFileCountRange
case Port < 1 || Port > 65535:
return ErrInvalidPort
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
err := ServePage(args)
if err != nil {
@ -81,10 +98,12 @@ func init() {
rootCmd.Flags().BoolVar(&Handlers, "handlers", false, "display registered handlers (for debugging)")
rootCmd.Flags().BoolVar(&Images, "images", false, "enable support for image files")
rootCmd.Flags().BoolVarP(&Info, "info", "i", false, "expose informational endpoints")
rootCmd.Flags().UintVar(&MaximumFileCount, "maximum-files", 1<<32-1, "skip directories with file counts above this value")
rootCmd.Flags().UintVar(&MinimumFileCount, "minimum-files", 1, "skip directories with file counts below this value")
rootCmd.Flags().Uint32Var(&PageLength, "page-length", 0, "pagination length for info pages")
rootCmd.Flags().Uint16VarP(&Port, "port", "p", 8080, "port to listen on")
rootCmd.Flags().IntVar(&MaxDirScans, "max-directory-scans", 32, "number of directories to scan at once")
rootCmd.Flags().IntVar(&MaxFileScans, "max-file-scans", 256, "number of files to scan at once")
rootCmd.Flags().IntVar(&MaxFileCount, "max-file-count", math.MaxInt32, "skip directories with file counts above this value")
rootCmd.Flags().IntVar(&MinFileCount, "min-file-count", 1, "skip directories with file counts below this value")
rootCmd.Flags().IntVar(&PageLength, "page-length", 0, "pagination length for info pages")
rootCmd.Flags().IntVarP(&Port, "port", "p", 8080, "port to listen on")
rootCmd.Flags().StringVar(&Prefix, "prefix", "/", "root path for http handlers (for reverse proxying)")
rootCmd.Flags().BoolVar(&Profile, "profile", false, "register net/http/pprof handlers")
rootCmd.Flags().BoolVarP(&Recursive, "recursive", "r", false, "recurse into subdirectories")

View File

@ -140,7 +140,7 @@ func serveStaticFile(paths []string, cache *fileCache, errorChannel chan<- error
}
if Verbose {
fmt.Printf("%s | Serve: %s (%s) to %s in %s%s\n",
fmt.Printf("%s | SERVE: %s (%s) to %s in %s%s\n",
startTime.Format(logDate),
filePath,
humanReadableSize(written),
@ -336,7 +336,7 @@ func serveMedia(paths []string, regexes *regexes, cache *fileCache, formats *typ
if format.Type() != "embed" {
if Verbose {
fmt.Printf("%s | Serve: %s (%s) to %s in %s\n",
fmt.Printf("%s | SERVE: %s (%s) to %s in %s\n",
startTime.Format(logDate),
path,
humanReadableSize(written),
@ -452,8 +452,17 @@ func ServePage(args []string) error {
mux := httprouter.New()
listenHost := net.JoinHostPort(Bind, strconv.Itoa(Port))
if Verbose {
fmt.Printf("%s | SERVE: Listening on %s...\n",
time.Now().Format(logDate),
listenHost,
)
}
srv := &http.Server{
Addr: net.JoinHostPort(Bind, strconv.Itoa(int(Port))),
Addr: listenHost,
Handler: mux,
IdleTimeout: 10 * time.Minute,
ReadTimeout: 5 * time.Second,
@ -507,10 +516,10 @@ func ServePage(args []string) error {
go func() {
for err := range errorChannel {
fmt.Printf("%s | Error: %v\n", time.Now().Format(logDate), err)
fmt.Printf("%s | ERROR: %v\n", time.Now().Format(logDate), err)
if ExitOnError {
fmt.Printf("%s | Error: Shutting down...\n", time.Now().Format(logDate))
fmt.Printf("%s | ERROR: Shutting down...\n", time.Now().Format(logDate))
srv.Shutdown(context.Background())
}