Simple S3-like server for development purposes, written in Go
1package main
2
3import (
4 "flag"
5 "log"
6 "net/http"
7 "os"
8)
9
10func addServerHeaders(serverName string) func(http.Handler) http.Handler {
11 return func(next http.Handler) http.Handler {
12 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
13 w.Header().Add("Server", serverName)
14 next.ServeHTTP(w, r)
15 })
16 }
17}
18
19func logging(logger *log.Logger) func(http.Handler) http.Handler {
20 return func(next http.Handler) http.Handler {
21 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
22 logger.Printf("%s %s %s", r.Method, r.URL.Path, r.RemoteAddr)
23 next.ServeHTTP(w, r)
24 })
25 }
26}
27
28func main() {
29 // TODO: Use slog instead
30 logger := log.New(os.Stderr, "gos3dir: ", log.LstdFlags)
31
32 flag.Parse()
33 if flag.NArg() == 0 {
34 logger.Fatalf("Pass directory name: gos3dir [path]")
35 os.Exit(1)
36 }
37
38 rootDir := flag.Arg(0)
39 if info, err := os.Stat(rootDir); err != nil {
40 logger.Fatalf("Error accessing root directory %q: %v", rootDir, err)
41 } else if !info.IsDir() {
42 logger.Fatalf("Path %q is not a directory", rootDir)
43 }
44 logger.Printf("Using root directory: %s", rootDir)
45
46 srv := &server{rootDir: rootDir, logger: logger}
47
48 // TODO: Support virtual addressing style https://docs.aws.amazon.com/cli/latest/topic/s3-config.html#addressing-style
49 // for now it assumes path style
50 http.HandleFunc("GET /{$}", srv.ls)
51 http.HandleFunc("GET /{bucket}", srv.ls)
52 http.HandleFunc("GET /{bucket}/{key...}", srv.cpGet)
53 http.HandleFunc("PUT /{bucket}", srv.mb)
54 http.HandleFunc("PUT /{bucket}/{key...}", srv.cpPut)
55 http.HandleFunc("DELETE /{bucket}", srv.rb)
56 http.HandleFunc("DELETE /{bucket}/{key...}", srv.rm)
57
58 handler := addServerHeaders("gos3dir")(logging(logger)(http.DefaultServeMux))
59
60 logger.Print("Server listening on :8041...")
61 // TODO: Graceful shutdown
62 if err := http.ListenAndServe(":8041", handler); err != nil {
63 logger.Fatal(err)
64 }
65}