// s3staticsites: servers http requests from an S3 bucket // Copyright (C) 2024 Finn Herzfeld // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . 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) w.Write([]byte("404 not found")) slog.Info("served 404", "domain", domain, "path", path) 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) }