-
Notifications
You must be signed in to change notification settings - Fork 833
Enforce query response size limit after decompression in query-frontend #6607
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
de3764a
fe4cc56
e6497e7
1fa4fd0
8092511
9eb4884
5456170
2664621
2a85bd7
8e6ddb9
7432331
fe3952c
50e9aaf
b30a088
ca25461
295d4b7
278ce54
386b727
20461fb
b83bc71
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ import ( | |
"bytes" | ||
"compress/gzip" | ||
"context" | ||
"encoding/binary" | ||
"fmt" | ||
"io" | ||
"net/http" | ||
|
@@ -14,7 +15,6 @@ import ( | |
|
||
"github.com/go-kit/log" | ||
"github.com/gogo/protobuf/proto" | ||
"github.com/golang/snappy" | ||
jsoniter "github.com/json-iterator/go" | ||
"github.com/opentracing/opentracing-go" | ||
otlog "github.com/opentracing/opentracing-go/log" | ||
|
@@ -26,6 +26,7 @@ import ( | |
"github.com/weaveworks/common/httpgrpc" | ||
|
||
"github.com/cortexproject/cortex/pkg/cortexpb" | ||
"github.com/cortexproject/cortex/pkg/util/limiter" | ||
"github.com/cortexproject/cortex/pkg/util/runutil" | ||
) | ||
|
||
|
@@ -448,7 +449,7 @@ type Buffer interface { | |
Bytes() []byte | ||
} | ||
|
||
func BodyBuffer(res *http.Response, logger log.Logger) ([]byte, error) { | ||
func BodyBytes(res *http.Response, responseSizeLimiter *limiter.ResponseSizeLimiter, logger log.Logger) ([]byte, error) { | ||
var buf *bytes.Buffer | ||
|
||
// Attempt to cast the response body to a Buffer and use it if possible. | ||
|
@@ -466,6 +467,11 @@ func BodyBuffer(res *http.Response, logger log.Logger) ([]byte, error) { | |
} | ||
} | ||
|
||
responseSize := getResponseSize(res, buf) | ||
if err := responseSizeLimiter.AddResponseBytes(responseSize); err != nil { | ||
return nil, httpgrpc.Errorf(http.StatusUnprocessableEntity, "%s", err.Error()) | ||
} | ||
|
||
// if the response is gzipped, lets unzip it here | ||
if strings.EqualFold(res.Header.Get("Content-Encoding"), "gzip") { | ||
gReader, err := gzip.NewReader(buf) | ||
|
@@ -475,15 +481,12 @@ func BodyBuffer(res *http.Response, logger log.Logger) ([]byte, error) { | |
defer runutil.CloseWithLogOnErr(logger, gReader, "close gzip reader") | ||
|
||
return io.ReadAll(gReader) | ||
} else if strings.EqualFold(res.Header.Get("Content-Encoding"), "snappy") { | ||
sReader := snappy.NewReader(buf) | ||
return io.ReadAll(sReader) | ||
Comment on lines
-478
to
-480
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How do we double check if this is safe to be removed? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We set the compression headers for instant and range query requests here https://github.com/afhassan/cortex/blob/be0fc7f216589a3e9cb66af85ee25cf192fd533b/pkg/querier/tripperware/query.go#L762 We currently only support gzip compression (default) or no compression. We never set any other
Snappy was added because we planned to potentially support it as well, but I think that can be left for a future PR to do. |
||
} | ||
|
||
return buf.Bytes(), nil | ||
} | ||
|
||
func BodyBufferFromHTTPGRPCResponse(res *httpgrpc.HTTPResponse, logger log.Logger) ([]byte, error) { | ||
func BodyBytesFromHTTPGRPCResponse(res *httpgrpc.HTTPResponse, logger log.Logger) ([]byte, error) { | ||
// if the response is gzipped, lets unzip it here | ||
headers := http.Header{} | ||
for _, h := range res.Headers { | ||
|
@@ -502,6 +505,15 @@ func BodyBufferFromHTTPGRPCResponse(res *httpgrpc.HTTPResponse, logger log.Logge | |
return res.Body, nil | ||
} | ||
|
||
func getResponseSize(res *http.Response, buf *bytes.Buffer) int { | ||
yeya24 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if strings.EqualFold(res.Header.Get("Content-Encoding"), "gzip") && len(buf.Bytes()) >= 4 { | ||
// GZIP body contains the size of the original (uncompressed) input data | ||
// modulo 2^32 in the last 4 bytes (https://www.ietf.org/rfc/rfc1952.txt). | ||
yeya24 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return int(binary.LittleEndian.Uint32(buf.Bytes()[len(buf.Bytes())-4:])) | ||
} | ||
return len(buf.Bytes()) | ||
} | ||
|
||
// UnmarshalJSON implements json.Unmarshaler. | ||
func (s *PrometheusData) UnmarshalJSON(data []byte) error { | ||
var queryData struct { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Does this method need to be renamed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function does not return body buffer. It returns body bytes array. I made that distinction when I split it into two functions, but even as one function it didn't make sense to me that it is called
BodyBuffer()
when it does not return a body buffer.I am okay with keeping the name or changing it to something else, but what is the reason for using
BodyBuffer()