package middlewares import ( "bytes" "io/fs" "log" "net/http" ) // FallbackFile serves a fallback file if the next handler returns a 404. // fs: The file system to serve the fallback file from. // name: The name of the fallback file. // next: The next handler to serve. func FallbackFile(fs fs.FS, name string, next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { bw := &buffedResponseWriter{ w, 0, bytes.NewBuffer([]byte{}), } next.ServeHTTP(bw, r) if bw.statusCode == http.StatusNotFound { bw.Clear() http.ServeFileFS(bw, r, fs, name) bw.WriteHeader(http.StatusOK) bw.Header().Add("Content-Type", "text/html; charset=utf-8") } bw.Apply() }) } type buffedResponseWriter struct { http.ResponseWriter statusCode int buff *bytes.Buffer } func (b *buffedResponseWriter) WriteHeader(code int) { b.statusCode = code } func (b *buffedResponseWriter) Write(p []byte) (int, error) { return b.buff.Write(p) } func (b *buffedResponseWriter) Clear() { b.buff.Reset() } func (b *buffedResponseWriter) Apply() { _, err := b.ResponseWriter.Write(b.buff.Bytes()) if err != nil { log.Println("Error while applying buffedResponseWriter: ", err) b.ResponseWriter.WriteHeader(http.StatusInternalServerError) } if b.statusCode != 200 { b.ResponseWriter.WriteHeader(b.statusCode) } }