feat: controlled error responses for HTTP honeypot

Add `withCustomError` middleware that intercepts HTTP error responses and replaces them with a custom error response.

This is used when the HTTP honeypot is configured to serve content from a directory. It ensures that all error responses from http.FileServerFS are controlled and predicatable.
This commit is contained in:
Ryan Smith
2025-04-15 14:44:26 -07:00
parent 60fe095dff
commit 90fbc24479
2 changed files with 49 additions and 1 deletions

View File

@@ -65,7 +65,7 @@ func determineConfig(cfg *config.Server) *responseConfig {
return &responseConfig{
mode: modeDirectory,
fsRoot: root,
fsHandler: http.FileServerFS(noDirectoryFS{root.FS()}),
fsHandler: withCustomError(http.FileServerFS(noDirectoryFS{root.FS()}), cfg.ErrorPagePath),
}
}

View File

@@ -0,0 +1,48 @@
package httpserver
import (
"net/http"
)
// withCustomError is a middleware that intercepts 4xx/5xx HTTP error responses
// and replaces them with a custom error response.
func withCustomError(next http.Handler, errorPath string) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
e := &errorInterceptor{origWriter: w, origRequest: r, errorPath: errorPath}
next.ServeHTTP(e, r)
})
}
// errorInterceptor intercepts HTTP responses to override error status codes
// and to serve a custom error response.
type errorInterceptor struct {
origWriter http.ResponseWriter
origRequest *http.Request
overridden bool
errorPath string
}
// WriteHeader intercepts error response codes (4xx or 5xx) to serve a custom
// error response.
func (e *errorInterceptor) WriteHeader(statusCode int) {
if statusCode >= 400 && statusCode <= 599 {
e.overridden = true
serveErrorPage(e.origWriter, e.origRequest, e.errorPath)
return
}
e.origWriter.WriteHeader(statusCode)
}
// Write writes the response body only if the response code was not overridden.
// Otherwise, the body is discarded.
func (e *errorInterceptor) Write(b []byte) (int, error) {
if !e.overridden {
return e.origWriter.Write(b)
}
return 0, nil
}
// Header returns the response headers from the original ResponseWriter.
func (e *errorInterceptor) Header() http.Header {
return e.origWriter.Header()
}