package main import ( "flag" "log" "net/http" "os" ) func addServerHeaders(serverName string) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Add("Server", serverName) next.ServeHTTP(w, r) }) } } func logging(logger *log.Logger) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { logger.Printf("%s %s %s", r.Method, r.URL.Path, r.RemoteAddr) next.ServeHTTP(w, r) }) } } func main() { // TODO: Use slog instead logger := log.New(os.Stderr, "gos3dir: ", log.LstdFlags) flag.Parse() if flag.NArg() == 0 { logger.Fatalf("Pass directory name: gos3dir [path]") os.Exit(1) } rootDir := flag.Arg(0) if info, err := os.Stat(rootDir); err != nil { logger.Fatalf("Error accessing root directory %q: %v", rootDir, err) } else if !info.IsDir() { logger.Fatalf("Path %q is not a directory", rootDir) } logger.Printf("Using root directory: %s", rootDir) srv := &server{rootDir: rootDir, logger: logger} // TODO: Support virtual addressing style https://docs.aws.amazon.com/cli/latest/topic/s3-config.html#addressing-style // for now it assumes path style http.HandleFunc("GET /{$}", srv.ls) http.HandleFunc("GET /{bucket}", srv.ls) http.HandleFunc("GET /{bucket}/{key...}", srv.cpGet) http.HandleFunc("PUT /{bucket}", srv.mb) http.HandleFunc("PUT /{bucket}/{key...}", srv.cpPut) http.HandleFunc("DELETE /{bucket}", srv.rb) http.HandleFunc("DELETE /{bucket}/{key...}", srv.rm) handler := addServerHeaders("gos3dir")(logging(logger)(http.DefaultServeMux)) logger.Print("Server listening on :8041...") // TODO: Graceful shutdown if err := http.ListenAndServe(":8041", handler); err != nil { logger.Fatal(err) } }