mirror of
https://github.com/ceph/ceph-csi.git
synced 2024-10-19 05:39:51 +00:00
177 lines
4.9 KiB
Go
177 lines
4.9 KiB
Go
|
package acceptencoding
|
||
|
|
||
|
import (
|
||
|
"compress/gzip"
|
||
|
"context"
|
||
|
"fmt"
|
||
|
"io"
|
||
|
|
||
|
"github.com/aws/smithy-go"
|
||
|
"github.com/aws/smithy-go/middleware"
|
||
|
smithyhttp "github.com/aws/smithy-go/transport/http"
|
||
|
)
|
||
|
|
||
|
const acceptEncodingHeaderKey = "Accept-Encoding"
|
||
|
const contentEncodingHeaderKey = "Content-Encoding"
|
||
|
|
||
|
// AddAcceptEncodingGzipOptions provides the options for the
|
||
|
// AddAcceptEncodingGzip middleware setup.
|
||
|
type AddAcceptEncodingGzipOptions struct {
|
||
|
Enable bool
|
||
|
}
|
||
|
|
||
|
// AddAcceptEncodingGzip explicitly adds handling for accept-encoding GZIP
|
||
|
// middleware to the operation stack. This allows checksums to be correctly
|
||
|
// computed without disabling GZIP support.
|
||
|
func AddAcceptEncodingGzip(stack *middleware.Stack, options AddAcceptEncodingGzipOptions) error {
|
||
|
if options.Enable {
|
||
|
if err := stack.Finalize.Add(&EnableGzip{}, middleware.Before); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if err := stack.Deserialize.Insert(&DecompressGzip{}, "OperationDeserializer", middleware.After); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
return stack.Finalize.Add(&DisableGzip{}, middleware.Before)
|
||
|
}
|
||
|
|
||
|
// DisableGzip provides the middleware that will
|
||
|
// disable the underlying http client automatically enabling for gzip
|
||
|
// decompress content-encoding support.
|
||
|
type DisableGzip struct{}
|
||
|
|
||
|
// ID returns the id for the middleware.
|
||
|
func (*DisableGzip) ID() string {
|
||
|
return "DisableAcceptEncodingGzip"
|
||
|
}
|
||
|
|
||
|
// HandleFinalize implements the FinalizeMiddleware interface.
|
||
|
func (*DisableGzip) HandleFinalize(
|
||
|
ctx context.Context, input middleware.FinalizeInput, next middleware.FinalizeHandler,
|
||
|
) (
|
||
|
output middleware.FinalizeOutput, metadata middleware.Metadata, err error,
|
||
|
) {
|
||
|
req, ok := input.Request.(*smithyhttp.Request)
|
||
|
if !ok {
|
||
|
return output, metadata, &smithy.SerializationError{
|
||
|
Err: fmt.Errorf("unknown request type %T", input.Request),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Explicitly enable gzip support, this will prevent the http client from
|
||
|
// auto extracting the zipped content.
|
||
|
req.Header.Set(acceptEncodingHeaderKey, "identity")
|
||
|
|
||
|
return next.HandleFinalize(ctx, input)
|
||
|
}
|
||
|
|
||
|
// EnableGzip provides a middleware to enable support for
|
||
|
// gzip responses, with manual decompression. This prevents the underlying HTTP
|
||
|
// client from performing the gzip decompression automatically.
|
||
|
type EnableGzip struct{}
|
||
|
|
||
|
// ID returns the id for the middleware.
|
||
|
func (*EnableGzip) ID() string {
|
||
|
return "AcceptEncodingGzip"
|
||
|
}
|
||
|
|
||
|
// HandleFinalize implements the FinalizeMiddleware interface.
|
||
|
func (*EnableGzip) HandleFinalize(
|
||
|
ctx context.Context, input middleware.FinalizeInput, next middleware.FinalizeHandler,
|
||
|
) (
|
||
|
output middleware.FinalizeOutput, metadata middleware.Metadata, err error,
|
||
|
) {
|
||
|
req, ok := input.Request.(*smithyhttp.Request)
|
||
|
if !ok {
|
||
|
return output, metadata, &smithy.SerializationError{
|
||
|
Err: fmt.Errorf("unknown request type %T", input.Request),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Explicitly enable gzip support, this will prevent the http client from
|
||
|
// auto extracting the zipped content.
|
||
|
req.Header.Set(acceptEncodingHeaderKey, "gzip")
|
||
|
|
||
|
return next.HandleFinalize(ctx, input)
|
||
|
}
|
||
|
|
||
|
// DecompressGzip provides the middleware for decompressing a gzip
|
||
|
// response from the service.
|
||
|
type DecompressGzip struct{}
|
||
|
|
||
|
// ID returns the id for the middleware.
|
||
|
func (*DecompressGzip) ID() string {
|
||
|
return "DecompressGzip"
|
||
|
}
|
||
|
|
||
|
// HandleDeserialize implements the DeserializeMiddlware interface.
|
||
|
func (*DecompressGzip) HandleDeserialize(
|
||
|
ctx context.Context, input middleware.DeserializeInput, next middleware.DeserializeHandler,
|
||
|
) (
|
||
|
output middleware.DeserializeOutput, metadata middleware.Metadata, err error,
|
||
|
) {
|
||
|
output, metadata, err = next.HandleDeserialize(ctx, input)
|
||
|
if err != nil {
|
||
|
return output, metadata, err
|
||
|
}
|
||
|
|
||
|
resp, ok := output.RawResponse.(*smithyhttp.Response)
|
||
|
if !ok {
|
||
|
return output, metadata, &smithy.DeserializationError{
|
||
|
Err: fmt.Errorf("unknown response type %T", output.RawResponse),
|
||
|
}
|
||
|
}
|
||
|
if v := resp.Header.Get(contentEncodingHeaderKey); v != "gzip" {
|
||
|
return output, metadata, err
|
||
|
}
|
||
|
|
||
|
// Clear content length since it will no longer be valid once the response
|
||
|
// body is decompressed.
|
||
|
resp.Header.Del("Content-Length")
|
||
|
resp.ContentLength = -1
|
||
|
|
||
|
resp.Body = wrapGzipReader(resp.Body)
|
||
|
|
||
|
return output, metadata, err
|
||
|
}
|
||
|
|
||
|
type gzipReader struct {
|
||
|
reader io.ReadCloser
|
||
|
gzip *gzip.Reader
|
||
|
}
|
||
|
|
||
|
func wrapGzipReader(reader io.ReadCloser) *gzipReader {
|
||
|
return &gzipReader{
|
||
|
reader: reader,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Read wraps the gzip reader around the underlying io.Reader to extract the
|
||
|
// response bytes on the fly.
|
||
|
func (g *gzipReader) Read(b []byte) (n int, err error) {
|
||
|
if g.gzip == nil {
|
||
|
g.gzip, err = gzip.NewReader(g.reader)
|
||
|
if err != nil {
|
||
|
g.gzip = nil // ensure uninitialized gzip value isn't used in close.
|
||
|
return 0, fmt.Errorf("failed to decompress gzip response, %w", err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return g.gzip.Read(b)
|
||
|
}
|
||
|
|
||
|
func (g *gzipReader) Close() error {
|
||
|
if g.gzip == nil {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
if err := g.gzip.Close(); err != nil {
|
||
|
g.reader.Close()
|
||
|
return fmt.Errorf("failed to decompress gzip response, %w", err)
|
||
|
}
|
||
|
|
||
|
return g.reader.Close()
|
||
|
}
|