I’m currently developing a Spring application and have encountered an issue while trying to post a message to a Websocket connection.
I’ve successfully set up a Websocket API in AWS API Gateway and can post to a specific connection using my IAM credentials via Postman.
However, when I attempt to do the same using the Java AWS SDK within my Spring application, I receive a 403 error.
Here is the snippet of code in Spring:
AwsSessionCredentials awsCreds = AwsSessionCredentials.create(
"***********",
"*********",
"**********"
);
AwsCredentialsProvider credentialsProvider = StaticCredentialsProvider.create(awsCreds);
ApiGatewayManagementApiClient testApiClient = ApiGatewayManagementApiClient.builder()
.region(Region.US_WEST_2)
.credentialsProvider(credentialsProvider)
.endpointOverride(new URI("https://api-id.execute-api.us-west-2.amazonaws.com/@connections/stage"))
.build();
String connectionId = "********";
GetConnectionResponse getResponse = testApiClient.getConnection(GetConnectionRequest.builder()
.connectionId(connectionId)
.build());
PostToConnectionResponse postResponse = testApiClient.postToConnection(PostToConnectionRequest.builder()
.connectionId(connectionId)
.data(SdkBytes.fromUtf8String("test message from Spring"))
.build());
The 1st request is attempting to get information about the connection.
The 2nd request is attempting to send a message to the connection. Both return a 403 error.
I know that my credentials have the correct permissions as I am able to directly send the same request via Postman with the same credentials and it works.
What is the issue?
TLDR: Use endpointOverride
value of https://{api-id}.execute-api.us-east-1.amazonaws.com/{stage}
You don't need to specify @connections
within the endpointOverride
.
Per the Javadoc, the SDK endpoint override follows the format of
https://{api-id}.execute-api.{region}.amazonaws.com/{stage}
not:
https://{api-id}.execute-api.{region}.amazonaws.com/@connections/{stage}
When using the SDK, the getConnection
& postToConnection
methods (as well as the deleteConnection
method) will automatically append @connections
to the endpoint of your deployed API.
GetConnectionRequestMarshaller.class
:
@SdkInternalApi
public class GetConnectionRequestMarshaller implements Marshaller<GetConnectionRequest> {
// ...
static {
SDK_OPERATION_BINDING = OperationInfo
.builder()
.requestUri("/@connections/{connectionId}")
.httpMethod(SdkHttpMethod.GET)
.hasExplicitPayloadMember(false)
.hasImplicitPayloadMembers(false)
.hasPayloadMembers(false)
.build();
}
}
PostToConnectionRequestMarshaller.class
:
@SdkInternalApi
public class PostToConnectionRequestMarshaller implements Marshaller<PostToConnectionRequest> {
// ...
static {
SDK_OPERATION_BINDING = OperationInfo
.builder()
.requestUri("/@connections/{connectionId}")
.httpMethod(SdkHttpMethod.POST)
.hasExplicitPayloadMember(true)
.hasImplicitPayloadMembers(false)
.hasPayloadMembers(true)
.build();
}
}
This should work instead:
URI endpointOverride = new URI("https://api-id.execute-api.us-west-2.amazonaws.com/stage");
ApiGatewayManagementApiClient testApiClient =
ApiGatewayManagementApiClient
.builder()
.region(Region.US_WEST_2)
.credentialsProvider(credentialsProvider)
.endpointOverride()
.build();
Even if you were to manually call the API without the SDK, the documented format is:
https://{api-id}.execute-api.{region}.amazonaws.com/{stage}/@connections/{connection_id}
/@connections/{stage}
won't work at all.