-
Notifications
You must be signed in to change notification settings - Fork 916
Async interceptor payload + S3 Checksums #823
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
Conversation
The sync payload is currently on the HTTP request, and interceptors modify the HTTP request payload by wrapping the contentStreamProvider in modifyHttpRequest. Why can't we do the same for async? |
I like the idea of having separate callbacks. I would change modifyHttpRequest to just return the headers and modify the content in a separate callback. SdkHttpRequest modifyHttpRequest(Context.ModifyHttpRequest context, ExecutionAttributes executionAttributes);
InputStream modifyHttpContent(Context.ModifyHttpContent context, ExecutionAttributes executionAttributes);
AsyncRequestBody modifyAsyncHttpContent(Context.ModifyHttpContent context, ExecutionAttributes executionAttributes); It would be better if we didn't add the thing being modified into the context until after the modify method so you would have something like this. (this would also allow composite interceptors since the thing you want to modify isn't in an immutable object). SdkHttpRequest modifyHttpRequest(SdkHttpRequest toModify, Context.ModifyHttpRequest context, ExecutionAttributes executionAttributes);
InputStream modifyHttpContent(InputStream toModify, Context.ModifyHttpContent context, ExecutionAttributes executionAttributes);
AsyncRequestBody modifyAsyncHttpContent(AsyncRequestBody toModify, Context.ModifyHttpContent context, ExecutionAttributes executionAttributes); and then whatever stage comes after modifying http content could have an Either<InputStream, AsyncRequestBody> for further access to the content. |
@millems, we certainly could wrap the SdkHttpContentPublisher and the TransformingAsyncResponseHandler. We would still need to either use a container object on modifyHttpRequest or expose separate methods in the interceptors. @shorea, I do like the idea of being consistent and splitting out the sync content into a separate method as well. |
@shorea +1 on your first option. Like the idea of splitting sync and async modifyHttpContents methods. Would it make sense to further split the For the second option, I feel it could be a bit confusing because the it can get the inputStream from both InputStream modifyHttpContent(InputStream toModify, Context.ModifyHttpContent context, ExecutionAttributes executionAttributes); |
So I was proposing not to have the content in the context at all until after that modifyHttpContent stage, so it's only in one location (the parameter in modifyHttpContent and the next context object in future callbacks). |
As long as sync and async are consistent, I don't mind too much.
|
Updated PR to include separate content interceptors. @millems, not sure I follow your latest comment. Are you saying we should put the response transformer and request body on the SdkHttpFullRequest/Response objects? |
@@ -204,6 +205,7 @@ protected SdkClientConfiguration finalizeChildConfiguration(SdkClientConfigurati | |||
private SdkClientConfiguration finalizeSyncConfiguration(SdkClientConfiguration config) { | |||
return config.toBuilder() | |||
.option(SdkClientOption.SYNC_HTTP_CLIENT, resolveSyncHttpClient(config)) | |||
.option(SdkClientOption.CLIENT_TYPE, "Sync") |
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.
Enum?
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.
Will add.
core/sdk-core/src/main/java/software/amazon/awssdk/core/interceptor/Context.java
Show resolved
Hide resolved
@@ -86,6 +106,9 @@ private Context() {} | |||
@ThreadSafe | |||
@SdkPublicApi | |||
public interface BeforeTransmission extends ModifyHttpRequest { | |||
RequestBody requestBody(); | |||
|
|||
AsyncRequestBody asyncRequestBody(); |
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.
Shouldn't this be in AfterMarshalling?
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.
Not if we have them as separate arguments in interceptors.
@@ -105,15 +144,16 @@ private Context() {} | |||
*/ | |||
@ThreadSafe | |||
@SdkPublicApi | |||
public interface ModifyHttpResponse extends AfterTransmission { | |||
public interface ModifyHttpResponseContent extends ModifyAsyncHttpResponseContent { | |||
Publisher responsePublisher(); | |||
} |
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 raw generics, please.
Shouldn't this be in AfterTransmision?
Where is the sync payload?
|
||
// Disable CS to avoid "Unused Import" error. If we use the FQCN in the Javadoc, we'll run into line length issues instead. | ||
// CHECKSTYLE:OFF | ||
// CHECKSTYLE:ON |
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.
Remove comments?
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.
IntelliJ moved these from where they should be around an import.
*/ | ||
default Publisher<ByteBuffer> modifyAsyncHttpResponseContent(Publisher<ByteBuffer> toModify, | ||
Context.ModifyAsyncHttpResponseContent context, | ||
ExecutionAttributes executionAttributes) { |
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 values to be modified aren't usually passed separately. Any reason we're changing that just for these?
.map(c -> invokeSafely(() -> IoUtils.toUtf8String(c))) | ||
.orElse(null); | ||
|
||
String policy = requestBody.contentStreamProvider() == null ? null : invokeSafely(() ->IoUtils.toUtf8String(requestBody.contentStreamProvider().newStream())); |
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.
run checkstyle
Builder request(SdkHttpFullRequest request); | ||
Builder request(SdkHttpRequest request); | ||
|
||
Builder contentStreamProvider(Optional<ContentStreamProvider> contentStreamProvider); |
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.
Don't accept an optional parameter.
@@ -26,5 +26,5 @@ | |||
@SdkPublicApi | |||
public interface InvokeableHttpRequest extends Callable<SdkHttpFullResponse>, Abortable { | |||
@Override | |||
SdkHttpFullResponse call() throws IOException; | |||
SdkHttpFullResponse call() throws IOException; // ExecuteResponse - sdkResponse + inputStream |
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.
TODO
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.
Ping TODO
@zoewangg @shorea The problem with removing http request from the AfterMarshalling context and http response from the AfterTransmission context, is that it makes afterMarshalling and afterTransmission much less useful. Those methods exist to see those values, so removing them means there's not really any reason to use them (or any way to see the values before they're modified by other interceptors). If we moved them to the first parameter of afterMarshalling (eg.) it would fix the problem, but three parameters for every method sounds like a lot... |
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.
Can you also update the execution interceptor protocol test to include the new methods?
* Implementation of {@link SdkChecksum} to calculate an MD5 checksum. | ||
*/ | ||
@SdkInternalApi | ||
public class MD5Checksum implements SdkChecksum { |
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.
Md5Checksum*
@@ -193,7 +198,18 @@ public void onError(Throwable error) { | |||
|
|||
@Override | |||
public void onStream(Publisher<ByteBuffer> publisher) { | |||
delegate.onStream(publisher); | |||
Publisher newPublisher = context.interceptorChain().modifyAsyncHttpResponse(context.interceptorContext() |
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.
Parameterize. Can you turn on intellij warnings for this?
...sdk-core/src/main/java/software/amazon/awssdk/core/client/handler/BaseSyncClientHandler.java
Show resolved
Hide resolved
|
||
RequestBody requestBody(); | ||
|
||
AsyncRequestBody asyncRequestBody(); |
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.
Javadoc. Should these be marked optional?
@@ -122,9 +122,9 @@ private HttpRequestBase wrapEntity(SdkHttpFullRequest request, | |||
* preparation for the retry. Eventually, these wrappers would | |||
* return incorrect validation result. | |||
*/ | |||
if (request.contentStreamProvider().isPresent()) { | |||
if (request.contentStreamProvider() != null && request.contentStreamProvider().isPresent()) { |
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.
Any method that returns optional should never return null. Sounds like there's a bug somewhere upstream.
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.
Ping - why is contentStreamProvider null?
...src/main/java/software/amazon/awssdk/services/apigateway/internal/AcceptJsonInterceptor.java
Show resolved
Hide resolved
private final SdkChecksum sdkChecksum; | ||
private final int contentLength; | ||
|
||
public ChecksumValidatingPublisher(Publisher publisher, |
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.
Parameterize.
@SdkInternalApi | ||
public class ChecksumValidatingPublisher implements SdkPublisher<ByteBuffer> { | ||
|
||
private final Publisher publisher; |
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.
Parameterize.
return this; | ||
} | ||
|
||
public Builder responsePublisher(Publisher responsePublisher) { |
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.
Parameterize.
...sdk-core/src/main/java/software/amazon/awssdk/core/client/handler/ClientExecutionParams.java
Show resolved
Hide resolved
@@ -26,5 +26,5 @@ | |||
@SdkPublicApi | |||
public interface InvokeableHttpRequest extends Callable<SdkHttpFullResponse>, Abortable { | |||
@Override | |||
SdkHttpFullResponse call() throws IOException; | |||
SdkHttpFullResponse call() throws IOException; // ExecuteResponse - sdkResponse + inputStream |
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.
Ping TODO
http-client-spi/src/main/java/software/amazon/awssdk/http/SdkHttpFullResponse.java
Show resolved
Hide resolved
@@ -122,9 +122,9 @@ private HttpRequestBase wrapEntity(SdkHttpFullRequest request, | |||
* preparation for the retry. Eventually, these wrappers would | |||
* return incorrect validation result. | |||
*/ | |||
if (request.contentStreamProvider().isPresent()) { | |||
if (request.contentStreamProvider() != null && request.contentStreamProvider().isPresent()) { |
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.
Ping - why is contentStreamProvider null?
b.putHeader("Content-Length", contentLength); | ||
} | ||
}).build(); | ||
return (SdkHttpFullRequest) SdkHttpFullRequest.builder() |
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.
Why is this (and other similar casts) needed?
@@ -25,6 +25,7 @@ | |||
import java.io.FileInputStream; | |||
import java.io.IOException; | |||
import java.nio.file.Path; | |||
import org.apache.log4j.BasicConfigurator; |
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.
Unused import
@@ -136,7 +136,7 @@ private static JsonMarshallerRegistry createMarshallerRegistry() { | |||
} | |||
|
|||
private SdkHttpFullRequest.Builder fillBasicRequestParams(OperationInfo operationInfo) { | |||
return ProtocolUtils.createSdkHttpRequest(operationInfo, endpoint) | |||
return (SdkHttpFullRequest.Builder) ProtocolUtils.createSdkHttpRequest(operationInfo, endpoint) |
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.
Needed cast?
@@ -26,5 +26,5 @@ | |||
@SdkPublicApi | |||
public interface InvokeableHttpRequest extends Callable<SdkHttpFullResponse>, Abortable { | |||
@Override | |||
SdkHttpFullResponse call() throws IOException; | |||
SdkHttpFullResponse call() throws IOException; // TODO: Change to container instad of SdkHttpFullResponse |
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 meant, can we actually do this?
@@ -416,7 +416,7 @@ private SdkHttpFullRequest validRequest() { | |||
} | |||
|
|||
private SdkHttpFullRequest.Builder validRequestBuilder() { | |||
return SdkHttpFullRequest.builder().protocol("http").host("localhost").method(SdkHttpMethod.GET); | |||
return (SdkHttpFullRequest.Builder) SdkHttpFullRequest.builder().protocol("http").host("localhost").method(SdkHttpMethod.GET); |
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.
Cast still needed?
b.putHeader("Content-Length", contentLength); | ||
} | ||
}).build(); | ||
return (SdkHttpFullRequest) SdkHttpFullRequest.builder() |
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.
Cast still needed?
result = result.copy(b -> b.httpRequest(interceptorResult) | ||
.asyncRequestBody(asyncRequestBody) | ||
.requestBody(requestBody) | ||
.build()); |
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.
build isn't needed
import software.amazon.awssdk.annotations.SdkPublicApi; | ||
|
||
@SdkPublicApi | ||
public class ExecuteResponse { |
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.
We should probably follow up with Andrew and see if we want to have a different name than ExecuteRequest and ExecuteResponse later.
e2949ea
to
ad39430
Compare
ad39430
to
774b98b
Compare
…04e9d0eb Pull request: release <- staging/142eddbf-4255-4523-bc71-564f04e9d0eb
Exposes the ability to modify the AsyncRequestBody and AsyncResponseTransformer.
Currently this is done by adding two new interceptors.
For AsyncRequestBody:
For AsyncResponseTransformer:
Possible other options include making modifyRequest return back a container object instead of an SdkRequest:
Where SdkRequestContext contains SdkRequest, AsyncRequestBody and AsyncResponseTransformer.
Or we could introduce an AsyncExecutionInterceptor interface that again either has two methods as in the PR or one method with a container object for async.
In general, trying to avoid potential confusion of when these interceptors are valid and when the values will be populated. If we use the approach in the PR, users of the Sync client should not use the new interceptors. If we go with the container approach for modifyRequest, then the asyncRequestBody and asyncResponseTransformer will be null.