package main import ( "compress/gzip" "flag" "fmt" "io" "log" "net/http" "strings" "git.teamworkapps.com/shortcut/enhancedfileserver" "github.com/andybalholm/brotli" "github.com/gorilla/mux" ) type config struct { listen string port int username string password string noindex bool nocache bool log bool } var c config func init() { flag.StringVar(&c.listen, "listen", "0.0.0.0", "IP address to listen.") flag.IntVar(&c.port, "port", 8000, "Listen port for interface (ports below 1024 may require super user privileges)") flag.StringVar(&c.username, "username", "", "Require this username tp access. Default is 'admin' if you specify a password.") flag.StringVar(&c.password, "password", "", "Require this password to access interface") flag.BoolVar(&c.noindex, "noindex", false, "Disable directory indexing") flag.BoolVar(&c.log, "log", false, "Enable simple request log") flag.BoolVar(&c.nocache, "nocache", false, "Set http headers to prevent caching of files") flag.Parse() } func main() { r := mux.NewRouter() if c.username == "" && c.password != "" { c.username = "admin" } if c.log { r.Use(queryLog) } if c.username != "" && c.password != "" { r.Use(basicAuth) } if c.nocache { r.Use(noCache) } // Always enable compression (both brotli and gzip) r.Use(compression) if c.noindex { r.PathPrefix("/").Handler(http.StripPrefix("/", enhancedfileserver.FileServerNoIndex(http.Dir("./")))) } else { r.PathPrefix("/").Handler(http.StripPrefix("/", enhancedfileserver.FileServer(http.Dir("./")))) } a := fmt.Sprintf("%s:%d", c.listen, c.port) log.Fatal(http.ListenAndServe(a, r)) } func noCache(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Add("Cache-control", "no-cache, no-store") next.ServeHTTP(w, r) }) } func basicAuth(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if c.username != "" && c.password != "" { user, pass, ok := r.BasicAuth() if !ok || user != c.username || pass != c.password { w.Header().Set("WWW-Authenticate", `Basic realm="mydomains"`) http.Error(w, "Unauthorized", http.StatusUnauthorized) return } } next.ServeHTTP(w, r) }) } func queryLog(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { log.Printf("%s - %s", r.RemoteAddr, r.URL) next.ServeHTTP(w, r) }) } // compressionWriter wraps http.ResponseWriter to provide compression type compressionWriter struct { http.ResponseWriter writer io.Writer } func (cw *compressionWriter) Write(b []byte) (int, error) { return cw.writer.Write(b) } // compression middleware that supports both brotli and gzip func compression(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Get the Accept-Encoding header acceptEncoding := r.Header.Get("Accept-Encoding") // Check if client supports brotli (preferred) if strings.Contains(acceptEncoding, "br") { w.Header().Set("Content-Encoding", "br") w.Header().Set("Vary", "Accept-Encoding") bw := brotli.NewWriter(w) defer bw.Close() cw := &compressionWriter{ ResponseWriter: w, writer: bw, } next.ServeHTTP(cw, r) return } // Check if client supports gzip if strings.Contains(acceptEncoding, "gzip") { w.Header().Set("Content-Encoding", "gzip") w.Header().Set("Vary", "Accept-Encoding") gw := gzip.NewWriter(w) defer gw.Close() cw := &compressionWriter{ ResponseWriter: w, writer: gw, } next.ServeHTTP(cw, r) return } // No compression supported, serve normally next.ServeHTTP(w, r) }) }