package main import ( "io" "net/http" minio "github.com/minio/minio-go/v7" "golang.org/x/exp/slog" ) func ListenAndServe(minioClient *minio.Client) error { slog.Info("starting http server", "bind", config.Bind) err := http.ListenAndServe(config.Bind, handler{minio: minioClient}) if err != nil { return err } return nil } type handler struct { minio *minio.Client } func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { domain := r.Host path := r.URL.Path if path == "" || path == "/" { path = "index.html" } object, err := h.minio.GetObject(r.Context(), domain, path, minio.GetObjectOptions{}) if err != nil { slog.Warn("error getting object from s3", "error", err, "domain", domain, "path", path) http.Error(w, err.Error(), http.StatusInternalServerError) return } stat, err := object.Stat() if err != nil { resp := minio.ToErrorResponse(err) if resp.StatusCode == http.StatusNotFound { // TODO: custom 404 page w.WriteHeader(http.StatusNotFound) return } slog.Warn("failed to stat object", "err", err, "bucket", domain, "path", path) http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Add("Content-Type", stat.ContentType) w.Header().Add("ETag", stat.ETag) n, err := io.Copy(w, object) if err != nil { slog.Warn("error writting response", "error", err, "domain", domain, "path", path) return } slog.Info("served request", "domain", domain, "path", path, "size_bytes", n) }