Limit Response Recorder memory (#20399)

Disable body recording for...

* admin inspect
* admin metrics
* profiling download

Also, if the recorded body is > 10MB, drop it.
This commit is contained in:
Klaus Post 2024-09-07 12:16:04 -07:00 committed by GitHub
parent 84e122c5c3
commit 9d5cdaa2e3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 29 additions and 11 deletions

View File

@ -159,14 +159,14 @@ func registerAdminRouter(router *mux.Router, enableConfigOps bool) {
// Info operations
adminRouter.Methods(http.MethodGet).Path(adminVersion + "/info").HandlerFunc(adminMiddleware(adminAPI.ServerInfoHandler, traceAllFlag, noObjLayerFlag))
adminRouter.Methods(http.MethodGet, http.MethodPost).Path(adminVersion + "/inspect-data").HandlerFunc(adminMiddleware(adminAPI.InspectDataHandler, noGZFlag, traceAllFlag))
adminRouter.Methods(http.MethodGet, http.MethodPost).Path(adminVersion + "/inspect-data").HandlerFunc(adminMiddleware(adminAPI.InspectDataHandler, noGZFlag, traceHdrsS3HFlag))
// StorageInfo operations
adminRouter.Methods(http.MethodGet).Path(adminVersion + "/storageinfo").HandlerFunc(adminMiddleware(adminAPI.StorageInfoHandler, traceAllFlag))
// DataUsageInfo operations
adminRouter.Methods(http.MethodGet).Path(adminVersion + "/datausageinfo").HandlerFunc(adminMiddleware(adminAPI.DataUsageInfoHandler, traceAllFlag))
// Metrics operation
adminRouter.Methods(http.MethodGet).Path(adminVersion + "/metrics").HandlerFunc(adminMiddleware(adminAPI.MetricsHandler, traceAllFlag))
adminRouter.Methods(http.MethodGet).Path(adminVersion + "/metrics").HandlerFunc(adminMiddleware(adminAPI.MetricsHandler, traceHdrsS3HFlag))
if globalIsDistErasure || globalIsErasure {
// Heal operations
@ -193,9 +193,9 @@ func registerAdminRouter(router *mux.Router, enableConfigOps bool) {
// Profiling operations - deprecated API
adminRouter.Methods(http.MethodPost).Path(adminVersion+"/profiling/start").HandlerFunc(adminMiddleware(adminAPI.StartProfilingHandler, traceAllFlag, noObjLayerFlag)).
Queries("profilerType", "{profilerType:.*}")
adminRouter.Methods(http.MethodGet).Path(adminVersion + "/profiling/download").HandlerFunc(adminMiddleware(adminAPI.DownloadProfilingHandler, traceAllFlag, noObjLayerFlag))
adminRouter.Methods(http.MethodGet).Path(adminVersion + "/profiling/download").HandlerFunc(adminMiddleware(adminAPI.DownloadProfilingHandler, traceHdrsS3HFlag, noObjLayerFlag))
// Profiling operations
adminRouter.Methods(http.MethodPost).Path(adminVersion + "/profile").HandlerFunc(adminMiddleware(adminAPI.ProfileHandler, traceAllFlag, noObjLayerFlag))
adminRouter.Methods(http.MethodPost).Path(adminVersion + "/profile").HandlerFunc(adminMiddleware(adminAPI.ProfileHandler, traceHdrsS3HFlag, noObjLayerFlag))
// Config KV operations.
if enableConfigOps {

View File

@ -26,6 +26,8 @@ import (
"net"
"net/http"
"time"
"github.com/klauspost/compress/gzip"
)
// ResponseRecorder - is a wrapper to trap the http response
@ -98,10 +100,16 @@ func (lrw *ResponseRecorder) Write(p []byte) (int, error) {
if lrw.TimeToFirstByte == 0 {
lrw.TimeToFirstByte = time.Now().UTC().Sub(lrw.StartTime)
}
gzipped := lrw.Header().Get("Content-Encoding") == "gzip"
if !gzipped && ((lrw.LogErrBody && lrw.StatusCode >= http.StatusBadRequest) || lrw.LogAllBody) {
// Always logging error responses.
lrw.body.Write(p)
if (lrw.LogErrBody && lrw.StatusCode >= http.StatusBadRequest) || lrw.LogAllBody {
// If body is > 10MB, drop it.
if lrw.bytesWritten+len(p) > 10<<20 {
lrw.LogAllBody = false
lrw.body = bytes.Buffer{}
} else {
// Always logging error responses.
lrw.body.Write(p)
}
}
if err != nil {
return n, err
@ -128,8 +136,16 @@ var gzippedBody = []byte("<GZIP>")
// Body - Return response body.
func (lrw *ResponseRecorder) Body() []byte {
if lrw.Header().Get("Content-Encoding") == "gzip" {
// ... otherwise we return the <GZIP> place holder
return gzippedBody
if lrw.body.Len() > 1<<20 {
return gzippedBody
}
r, err := gzip.NewReader(&lrw.body)
if err != nil {
return gzippedBody
}
defer r.Close()
b, _ := io.ReadAll(io.LimitReader(r, 10<<20))
return b
}
// If there was an error response or body logging is enabled
// then we return the body contents
@ -152,7 +168,9 @@ func (lrw *ResponseRecorder) WriteHeader(code int) {
// Flush - Calls the underlying Flush.
func (lrw *ResponseRecorder) Flush() {
lrw.ResponseWriter.(http.Flusher).Flush()
if flusher, ok := lrw.ResponseWriter.(http.Flusher); ok {
flusher.Flush()
}
}
// Size - returns the number of bytes written