request_compression.go (3331B)
1 // Package requestcompression implements runtime support for smithy-modeled 2 // request compression. 3 // 4 // This package is designated as private and is intended for use only by the 5 // smithy client runtime. The exported API therein is not considered stable and 6 // is subject to breaking changes without notice. 7 package requestcompression 8 9 import ( 10 "bytes" 11 "context" 12 "fmt" 13 "io" 14 15 "github.com/aws/smithy-go/middleware" 16 "github.com/aws/smithy-go/transport/http" 17 ) 18 19 const MaxRequestMinCompressSizeBytes = 10485760 20 21 // Enumeration values for supported compress Algorithms. 22 const ( 23 GZIP = "gzip" 24 ) 25 26 type compressFunc func(io.Reader) ([]byte, error) 27 28 var allowedAlgorithms = map[string]compressFunc{ 29 GZIP: gzipCompress, 30 } 31 32 // AddRequestCompression add requestCompression middleware to op stack 33 func AddRequestCompression(stack *middleware.Stack, disabled bool, minBytes int64, algorithms []string) error { 34 return stack.Serialize.Add(&requestCompression{ 35 disableRequestCompression: disabled, 36 requestMinCompressSizeBytes: minBytes, 37 compressAlgorithms: algorithms, 38 }, middleware.After) 39 } 40 41 type requestCompression struct { 42 disableRequestCompression bool 43 requestMinCompressSizeBytes int64 44 compressAlgorithms []string 45 } 46 47 // ID returns the ID of the middleware 48 func (m requestCompression) ID() string { 49 return "RequestCompression" 50 } 51 52 // HandleSerialize gzip compress the request's stream/body if enabled by config fields 53 func (m requestCompression) HandleSerialize( 54 ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler, 55 ) ( 56 out middleware.SerializeOutput, metadata middleware.Metadata, err error, 57 ) { 58 if m.disableRequestCompression { 59 return next.HandleSerialize(ctx, in) 60 } 61 // still need to check requestMinCompressSizeBytes in case it is out of range after service client config 62 if m.requestMinCompressSizeBytes < 0 || m.requestMinCompressSizeBytes > MaxRequestMinCompressSizeBytes { 63 return out, metadata, fmt.Errorf("invalid range for min request compression size bytes %d, must be within 0 and 10485760 inclusively", m.requestMinCompressSizeBytes) 64 } 65 66 req, ok := in.Request.(*http.Request) 67 if !ok { 68 return out, metadata, fmt.Errorf("unknown request type %T", req) 69 } 70 71 for _, algorithm := range m.compressAlgorithms { 72 compressFunc := allowedAlgorithms[algorithm] 73 if compressFunc != nil { 74 if stream := req.GetStream(); stream != nil { 75 size, found, err := req.StreamLength() 76 if err != nil { 77 return out, metadata, fmt.Errorf("error while finding request stream length, %v", err) 78 } else if !found || size < m.requestMinCompressSizeBytes { 79 return next.HandleSerialize(ctx, in) 80 } 81 82 compressedBytes, err := compressFunc(stream) 83 if err != nil { 84 return out, metadata, fmt.Errorf("failed to compress request stream, %v", err) 85 } 86 87 var newReq *http.Request 88 if newReq, err = req.SetStream(bytes.NewReader(compressedBytes)); err != nil { 89 return out, metadata, fmt.Errorf("failed to set request stream, %v", err) 90 } 91 *req = *newReq 92 93 if val := req.Header.Get("Content-Encoding"); val != "" { 94 req.Header.Set("Content-Encoding", fmt.Sprintf("%s, %s", val, algorithm)) 95 } else { 96 req.Header.Set("Content-Encoding", algorithm) 97 } 98 } 99 break 100 } 101 } 102 103 return next.HandleSerialize(ctx, in) 104 }