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.

Add downloads

+36
+1
gos3dir.go
··· 51 51 // for now it assumes path style 52 52 http.HandleFunc("GET /{$}", srv.ls) 53 53 http.HandleFunc("GET /{bucket}", srv.ls) 54 + http.HandleFunc("GET /{bucket}/{key...}", srv.cpGet) 54 55 http.HandleFunc("PUT /{bucket}", srv.mb) 55 56 http.HandleFunc("PUT /{bucket}/{key...}", srv.cpPut) 56 57 http.HandleFunc("DELETE /{bucket}", srv.rb)
+35
server.go
··· 3 3 import ( 4 4 "encoding/xml" 5 5 "errors" 6 + "fmt" 6 7 "io" 7 8 "io/fs" 8 9 "log" 9 10 "net/http" 10 11 "os" 11 12 "path/filepath" 13 + "strconv" 12 14 "strings" 13 15 "time" 14 16 ) ··· 79 81 Contents: objects, 80 82 } 81 83 return result, nil 84 + } 85 + 86 + func (s *server) cpGet(w http.ResponseWriter, r *http.Request) { 87 + root, err := os.OpenRoot(s.rootDir) 88 + if err != nil { 89 + http.Error(w, err.Error(), http.StatusInternalServerError) 90 + } 91 + defer root.Close() 92 + 93 + path := filepath.Join(r.PathValue("bucket"), r.PathValue("key")) 94 + fileInfo, err := root.Stat(path) 95 + if err != nil { 96 + if errors.Is(err, fs.ErrNotExist) { 97 + http.Error(w, fmt.Sprintf("Key %s does not exist", r.PathValue("key")), http.StatusNotFound) 98 + } else { 99 + http.Error(w, err.Error(), http.StatusInternalServerError) 100 + } 101 + return 102 + } 103 + 104 + w.Header().Set("Content-Type", "binary/octet-stream") 105 + w.Header().Set("Content-Length", strconv.FormatInt(fileInfo.Size(), 10)) 106 + w.Header().Set("Last-Modified", fileInfo.ModTime().Format(time.RFC3339)) 107 + 108 + if r.Method == http.MethodHead { 109 + return 110 + } 111 + 112 + file, err := root.Open(path) 113 + if err != nil { 114 + http.Error(w, err.Error(), http.StatusInternalServerError) 115 + } 116 + io.Copy(w, file) 82 117 } 83 118 84 119 func (s *server) ls(w http.ResponseWriter, r *http.Request) {