-
Notifications
You must be signed in to change notification settings - Fork 658
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
Port v1 SDK customization for s3 HTTP PUT request #2051
Conversation
…mization middleware for s3 client http PUT operation larger than 2MB or with unknown size Body.
|
||
// returns true if service is either s3 or s3 control and needs s3 customization | ||
private static boolean requiresS3Customization(Model model, ServiceShape service) { | ||
return S3ModelUtils.isServiceS3(model, service) || S3ModelUtils.isServiceS3Control(model, service); |
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.
fix: Probably doesn't need to apply to s3control
public List<RuntimeClientPlugin> getClientPlugins() { | ||
return ListUtils.of( | ||
RuntimeClientPlugin.builder() | ||
.servicePredicate(S3100Continue::requiresS3Customization) |
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.
fix: IIRC this builder can also specify an operation predicate, we should use that to restrict this to just the operations that we want to use it on (PutObject
, UploadPart
, etc)
config/go.mod
Outdated
github.com/aws/aws-sdk-go-v2/service/sso v1.12.4 | ||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.4 | ||
github.com/aws/aws-sdk-go-v2/service/sts v1.18.5 | ||
github.com/aws/aws-sdk-go-v2 v1.17.6 |
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.
fix: There are way too many changes to go.mod
and go_module_metadata.go
un-related to the intent of this PR. We should figure out why these changed/revert them. It makes it very difficult to follow the PR otherwise
return out, metadata, fmt.Errorf("unknown request type %T", req) | ||
} | ||
|
||
if req.Method == "PUT" && (req.ContentLength == -1 || (req.ContentLength == 0 && req.Body != nil) || req.ContentLength >= 1024*1024*2) { |
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.
comment: If we scope the customization correctly in codegen to only apply to specific operations we can probably remove the req.Method
check here.
fix: Change limit (2MB) to a private constant in this file
) | ||
|
||
// unit test for service/internal/s3shared/s3100continue.go | ||
func TestAdd100ContinueHttpHeader(t *testing.T) { |
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.
fix: Is there not a way to have the test live closer to where the code that defines it is (e.g. s3shared
)? It would also seem like we don't really need to spinup a test server for this and can instead directly test the middleware (example from smithy-go).
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.
If I put it under s3shared
containing middleware, circular dependencies occur since s3shared
and s3
depends on each other in that case due to the test.
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.
Right but if you don't make the test depend on the s3 client and instead just test the middleware in isolation then it should be fine (see the example tests linked in previous comment)
...codegen/src/main/java/software/amazon/smithy/aws/go/codegen/customization/S3100Continue.java
Show resolved
Hide resolved
* Add middleware, which adds {Expect: 100-continue} header for s3 client HTTP PUT request larger than 2MB | ||
* or with unknown size streaming bodies, during operation builder step | ||
*/ | ||
public class S3100Continue implements GoIntegration { |
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.
fix: Not sure where to make this comment but we ran into an issue with this header being set and signing. See this PR for more details. Can you verify that s3 put object requests with transfer acceleration enabled don't run into issues when this header is present? We may need to special case it in signing as we did in Kotlin.
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.
I'm confused about the test design of this validation. Since UseAccelerate and SupportsAccelerate is configured in UpdateEndpoints at Serialize step in stack, is it possible to wire up inner middleware like processARNResource
, s3ObjectLambdaEndpoint
etc with current s3100Continue middleware in isolation in unit test to check issues?
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.
No need to check it in a unit test. Just test the actual real S3 service with your customization enabled and UseAccelerate
enabled (might need to configure it on the bucket).
…zation # Conflicts: # config/go.mod
…on, exempt Expect header from signV4
service/s3/api_client.go
Outdated
@@ -80,6 +80,11 @@ type Options struct { | |||
// Configures the events that will be sent to the configured logger. | |||
ClientLogMode aws.ClientLogMode | |||
|
|||
// The threshold ContentLength for HTTP PUT request to receive {Expect: | |||
// 100-continue} header. When set to -1, this header will be opt out of the | |||
// operation request; when set to 0, the thresholdwill be set to default 2MB |
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 threshold will (space)
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.
Couple small fixes but this looks pretty close.
func TestIgnoredHeaders(t *testing.T) { | ||
cases := map[string]struct { | ||
Header string | ||
ExpectIgnored bool |
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: Slightly confusing to understand the expectations due to the inversion. Originally interpreted the test cases as "expect the header to be ignored" but that isn't how it's used. Might consider renaming or changing the assertion code
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.
Agree, the initial ExcludeList
rule within v4 is also a little confusing since it is valid when inner rule is invalid. I have changed the ExpectIgnored
to be true if the header is ignored.
public List<RuntimeClientPlugin> getClientPlugins() { | ||
return ListUtils.of( | ||
RuntimeClientPlugin.builder() | ||
.operationPredicate((model, service, operation) -> isS3Service(model, service) && operation. |
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.
fix: We probably really only care about the PutObject
and UploadPart
operations. The other APIs don't have large binary payloads where an Expect
header makes any difference (and may not be supported by the service for those operations IDK).
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.
Question: Can service and operation predicates be combined such that you can move the isS3Service
check to servicePredicate(...)
?
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.
For service/operation predicates combination, if I put isS3Service within servicePredicate() , no matter I put that before or after the servicePredicate , it will put the add100Continue middleware within some operation that we don't want (outside s3 or within s3's other operation). Thus I think it might be better to keep the service check within operation predicate like S3GetBucketLocation
.type(SymbolUtils.createValueSymbolBuilder("int64") | ||
.putProperty(SymbolUtils.GO_UNIVERSE_TYPE, true) | ||
.build()) | ||
.documentation("The threshold ContentLength for HTTP PUT request to receive {Expect: 100-continue} header. " + |
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:
The threshold ContentLength in bytes ...
.putProperty(SymbolUtils.GO_UNIVERSE_TYPE, true) | ||
.build()) | ||
.documentation("The threshold ContentLength for HTTP PUT request to receive {Expect: 100-continue} header. " + | ||
"When set to -1, this header will be opt out of the operation request; when set to 0, the threshold" + |
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.
fix:
Setting to -1 will disable adding the
Expect
header to requests ...
return out, metadata, fmt.Errorf("unknown request type %T", req) | ||
} | ||
|
||
if req.ContentLength == -1 || (req.ContentLength == 0 && req.Body != nil) || req.ContentLength >= sizeLimit { |
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.
Question: When do we hit req.ContentLength == 0 && req.Body != nil
case?
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.
I haven't met such case in real code, it is judged only because the initial issue put forward that "Need to port the V1 SDK customization that set the Expect: 100-continue header for HTTP PUT operations that were larger 2 MB or streaming bodies where the content-length is unknown." According to http.Request
comment about ContentLength
, "For client requests, a value of 0 with a non-nil Body is also treated as unknown."
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.
Fix and ship.
{ | ||
"id": "1d119df5-78fb-4a57-a293-4d38ea28aaa5", | ||
"type": "feature", | ||
"description": "enable s3100continue header config, exempt it from sigV4", |
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.
fix: There should only be one changelog entry for this entire PR/change.
private static final String ADD_100Continue_Header = "add100Continue"; | ||
private static final String ADD_100Continue_Header_INTERNAL = "Add100Continue"; | ||
private static final String Continue_Client_Option = "ContinueHeaderThresholdBytes"; | ||
private static final Set<String> Put_Op_Set = new HashSet<>(Arrays.asList("PutObject", "UploadPart")); |
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: Use the full shape ID rather than just the name (e.g. com.amazonaws.s3#PutObject
or whatever it actually is).
) | ||
|
||
const s3100ContinueID = "S3100Continue" | ||
const defaultLimit int64 = 1024 * 1024 * 2 |
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: default100ContinueThresholdBytes
or similar.
Add {Expect: 100-continue} http header customization middleware for s3 client HTTP PUT request larger than 2MB or with unknown-size body, solve #1451