Simple S3-like server for development purposes, written in Go
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Use root directory

+39 -19
+39 -19
gos3dir.go
··· 2 2 3 3 import ( 4 4 "errors" 5 + "flag" 5 6 "io" 6 7 "io/fs" 7 8 "log" ··· 9 10 "os" 10 11 "path/filepath" 11 12 ) 12 - 13 - const rootDir = "." // TODO: Turn into a parameter 14 13 15 14 func addServerHeaders(serverName string) func(http.Handler) http.Handler { 16 15 return func(next http.Handler) http.Handler { ··· 30 29 } 31 30 } 32 31 33 - func ls(w http.ResponseWriter, r *http.Request) { 34 - log.Printf("Path %s", r.PathValue("bucket")) 32 + type server struct { 33 + rootDir string 34 + logger *log.Logger 35 + } 36 + 37 + func (s *server) ls(w http.ResponseWriter, r *http.Request) { 38 + s.logger.Printf("Path %s", r.PathValue("bucket")) 35 39 // TODO: Probably requires actual XML 36 40 w.WriteHeader(http.StatusNotImplemented) 37 41 } 38 42 39 - func mb(w http.ResponseWriter, r *http.Request) { 40 - root, err := os.OpenRoot(rootDir) 43 + func (s *server) mb(w http.ResponseWriter, r *http.Request) { 44 + root, err := os.OpenRoot(s.rootDir) 41 45 if err != nil { 42 46 http.Error(w, err.Error(), http.StatusInternalServerError) 43 47 } ··· 58 62 w.WriteHeader(http.StatusOK) 59 63 } 60 64 61 - func rb(w http.ResponseWriter, r *http.Request) { 62 - root, err := os.OpenRoot(rootDir) 65 + func (s *server) rb(w http.ResponseWriter, r *http.Request) { 66 + root, err := os.OpenRoot(s.rootDir) 63 67 if err != nil { 64 68 http.Error(w, err.Error(), http.StatusInternalServerError) 65 69 } ··· 90 94 w.WriteHeader(http.StatusOK) 91 95 } 92 96 93 - func cp(w http.ResponseWriter, r *http.Request) { 94 - root, err := os.OpenRoot(rootDir) 97 + func (s *server) cp(w http.ResponseWriter, r *http.Request) { 98 + root, err := os.OpenRoot(s.rootDir) 95 99 if err != nil { 96 100 http.Error(w, err.Error(), http.StatusInternalServerError) 97 101 } ··· 132 136 w.WriteHeader(http.StatusOK) 133 137 } 134 138 135 - func rm(w http.ResponseWriter, r *http.Request) { 136 - root, err := os.OpenRoot(rootDir) 139 + func (s *server) rm(w http.ResponseWriter, r *http.Request) { 140 + root, err := os.OpenRoot(s.rootDir) 137 141 if err != nil { 138 142 http.Error(w, err.Error(), http.StatusInternalServerError) 139 143 } ··· 150 154 } 151 155 152 156 func main() { 153 - // TODO: (Claude) "S3 returns ETags for uploaded objects. Consider adding MD5 hashing" 154 - http.HandleFunc("GET /{bucket}", ls) 155 - http.HandleFunc("PUT /{bucket}", mb) 156 - http.HandleFunc("PUT /{bucket}/{key...}", cp) 157 - http.HandleFunc("DELETE /{bucket}", rb) 158 - http.HandleFunc("DELETE /{bucket}/{key...}", rm) 157 + logger := log.New(os.Stderr, "gos3dir: ", log.LstdFlags) 158 + 159 + flag.Parse() 160 + if flag.NArg() == 0 { 161 + logger.Fatalf("Pass directory name: gos3dir [path]") 162 + os.Exit(1) 163 + } 164 + 165 + rootDir := flag.Arg(0) 166 + if info, err := os.Stat(rootDir); err != nil { 167 + logger.Fatalf("Error accessing root directory %q: %v", rootDir, err) 168 + } else if !info.IsDir() { 169 + logger.Fatalf("Path %q is not a directory", rootDir) 170 + } 171 + logger.Printf("Using root directory: %s", rootDir) 159 172 160 - logger := log.New(os.Stderr, "gos3dir: ", log.LstdFlags) 173 + srv := &server{rootDir: rootDir, logger: logger} 174 + 175 + // TODO: (Claude) "S3 returns ETags for uploaded objects. Consider adding MD5 hashing" 176 + http.HandleFunc("GET /{bucket}", srv.ls) 177 + http.HandleFunc("PUT /{bucket}", srv.mb) 178 + http.HandleFunc("PUT /{bucket}/{key...}", srv.cp) 179 + http.HandleFunc("DELETE /{bucket}", srv.rb) 180 + http.HandleFunc("DELETE /{bucket}/{key...}", srv.rm) 161 181 162 182 handler := addServerHeaders("gos3dir")(logging(logger)(http.DefaultServeMux)) 163 183