When making a DELETE REST request using the Amplify SDK if you include a body with it the SDK throws an exception. When you create a DELETE request without a body Okhttp3 adds a body of byte[0]. Later down the chain the AppSyncSigV4SignerInterceptor sets the content type to JSON as it only checks if the body is null.
I'm using version 1.6.6. of Amplify Android.
override fun makeDeleteCall(path: String): Completable {
return Completable.create {
Amplify.API.delete(
createCallOptions(path),
it.consumeSuccess(),
it.consumeError()
)
}
}
private fun createCallOptions(path: String): RestOptions {
return RestOptions.builder().run {
addPath(path)
build()
}
}
With body:
ApiException{message=HTTP method does not support data object! DELETE, cause=null, recoverySuggestion=Try sending the request without any data in the options.}
Without body:
java.io.IOExceptionL: Network request not successful. Code{statusCode=400}: {"statusCode":400,"code":"FST_ERR_CTP_EMPTY_JSON_BODY","error":"Bad Request","message":"Body cannot be empty when content-type is set to 'application/json'"}
The bug is that creating a DELETE request without a body creates an invalid request. If you do that you pass the check the Amplify SDK does in com.amplifyframework.api.aws.AWSApiPlugin line 605-617. Then it goes on to OkHttp which in okhttp3.Request on line 239-241 (method is delete()) does:
public Builder delete(@Nullable RequestBody body) {
return method("DELETE", body);
}
public Builder delete() {
return delete(Util.EMPTY_REQUEST);
}
and in the okhttp3.internal.Util class on lines 59 and 63:
public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
public static final RequestBody EMPTY_REQUEST = RequestBody.create(null, EMPTY_BYTE_ARRAY);
So later on in com.amplifyframework.api.aws.sigv4.AppSyncSigV4SignerInterceptor the following happens on line 229-231:
//Set the URL and Method
final RequestBody requestBody = req.body() != null ?
RequestBody.create(bodyBytes, JSON_MEDIA_TYPE) : null;
This adds the header content-type: application/json to the request. Which then leads to request like the following:
:method: DELETE
:path: xxxxxxxx
:authority: xxxxxxxxxxxxxxx
:scheme: https
authorization: AWS4-HMAC-SHA256 Credential=xxxxxxxxxxx/20201207/us-east-1/execute-api/aws4_request, SignedHeaders=host;x-amz-date;x-amz-security-token, Signature=xxxxxxxxxx
x-amz-date: 20201207T150852Z
x-amz-security-token: xxxxxxxxxxxx
content-type: application/json
content-length: 0
accept-encoding: gzip
user-agent: amplify-android/1.6.6 (Android 10; xxxxxxxxx; en_US)
Where the content-type is set to application/json but the content-length is 0.
The Okhttp3 version is 4.9.0.
Sending anything in the body like "" or {} works. It is what I do to send a post request without a body. However this wont be allowed by the Amplify SDK.
From the look of things changing the
//Set the URL and Method
final RequestBody requestBody = req.body() != null ?
RequestBody.create(bodyBytes, JSON_MEDIA_TYPE) : null;
statement in com.amplifyframework.api.aws.sigv4.AppSyncSigV4SignerInterceptor to something like
final RequestBody requestBody;
if (req.body() != null && req.body().length > 0) {
requestBody = RequestBody.create(bodyBytes, JSON_MEDIA_TYPE);
} else {
requestBody = null;
}
should do the trick.
Thanks for reporting this issue + the PR! I will be closing this issue since the fix has been merged. The changes will go live in our next release.
Most helpful comment
From the look of things changing the
statement in
com.amplifyframework.api.aws.sigv4.AppSyncSigV4SignerInterceptorto something likeshould do the trick.