Reputation: 71
I have created a Http subscription in SNS topic . The url mentioned in the SNS subscription is an EC2 instance.
Need to know where the SNS subscription request will be received in the EC2 instance.
What are the applications I have to install in EC2 for subscribing to the http request. the port used is 8080.
Upvotes: 1
Views: 4221
Reputation: 5502
As described in the Amazon Doc for SNS HTTP(S) based subscription...more
Before you subscribe to your HTTP or HTTPS endpoint to a topic, you must make sure that the HTTP or HTTPS endpoint can handle the HTTP POST requests that Amazon SNS uses to send the subscription confirmation and notification messages. When you subscribe to an HTTP endpoint, Amazon SNS sends it a subscription confirmation request. Your endpoint must be prepared to receive and process this request when you create the subscription because Amazon SNS sends this request at that time. Amazon SNS will not send notifications to the endpoint until you confirm the subscription. Once you confirm the subscription, Amazon SNS will send notifications to the endpoint when a publish action is performed on the subscribed topic.
Until the SubscriptionConfirmation request is being processed, the status always in Pending confirmation status and will not receive any Notification message.
Here is the code snippet to handle SubscriptionConfirmation, Notification and the UnsubscribeConfirmation messageType requests automatically and Content-Type for the above requests which triggered by AWS SNS always text/plain format.
The main advantage of using Spring-boot for SNS consumer with the below code is you don't need to rely on any AWS specify configurations and it's dependencies. This is a pragmatic approach if your microservices built with spring-boot
and not with spring-cloud-aws-messaging
. The following steps are required in your consumer-side application.
Add the below annotation to the request handler method to handle SubscriptionConfirmation and UnsubscribeConfirmation messageTypes.
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface SNSSubscriptionUnSubscriptionConfirmation {
}
The below Aspect will be triggered, If the request handler method annotated with @SNSSubscriptionUnSubscriptionConfirmation,
which will take care of the SubscriptionConfirmation and UnsubscribeConfirmation messageTypes based on the Http(s) request header x-amz-sns-message-type
.
@Aspect
@Component
@Slf4j
public class SNSSubscriptionUnSubscriptionActivation {
@Before(value = "@annotation(SNSSubscriptionUnSubscriptionConfirmation)")
public void SNSSubscriptionUnSubscriptionConfirmationActivation(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
HttpServletRequest httpServletRequest = (HttpServletRequest)args[0];
String requestBody = (String)args[1];
String messageType = httpServletRequest.getHeader("x-amz-sns-message-type");
String topicArn = httpServletRequest.getHeader("x-amz-sns-topic-arn");
if(!StringUtils.isEmpty(messageType)){
if("SubscriptionConfirmation".equals(messageType)){
activateSNSSubscriptionUnSubscription(requestBody, messageType, topicArn, "SubscribeURL");
} else if("UnsubscribeConfirmation".equals(messageType)){
activateSNSSubscriptionUnSubscription(requestBody, messageType, topicArn, "UnsubscribeURL");
}
}
}
private void activateSNSSubscriptionUnSubscription(String requestBody, final String messageType, String topicArn, String subscribeUnsubscribeURLKey) {
log.info(messageType + " payload: {}", requestBody);
JsonMapper mapper = JsonMapper.builder()
.configure(JsonReadFeature.ALLOW_UNQUOTED_FIELD_NAMES, true)
.build();
try {
Map<String, String> maps = mapper.readValue(requestBody, new TypeReference<Map<String, String>>() {
});
String subscribeUnsubscribeURL = maps.get(subscribeUnsubscribeURLKey);
RestTemplate restTemplate = new RestTemplate();
//Manually activating the subscribe and UnsubscribeURL requests by making direct HTTP call using rest controller
ResponseEntity<Void> response = restTemplate.exchange(subscribeUnsubscribeURL, HttpMethod.GET, null, Void.class);
if (response.getStatusCode().is2xxSuccessful())
log.info("topicArn: {} messageType: {} Successful: {}", topicArn, messageType, response.getStatusCode());
else {
log.error("topicArn: {} messageType: {} failure Status: {}", topicArn, messageType, response.getStatusCode());
}
} catch (JsonProcessingException e){
log.error("topicArn: {} messageType: {} failure error: {}", topicArn, messageType, e.getMessage());
}catch(HttpClientErrorException e) {
log.error("topicArn: {} messageType: {} failure error: {}", topicArn, messageType, e.getResponseBodyAsString());
}
}
}
The controller's handler method handles Notification messageType because the remaining two messageTypes are handled by SNSSubscriptionUnSubscriptionActivation
as the request handler method annotated with @SNSSubscriptionUnSubscriptionConfirmation()
.
@Slf4j
@RestController
public class AWSSNSConsumerController {
@PostMapping(value = "subscribed-endpoint", consumes = MediaType.TEXT_PLAIN_VALUE)
@SNSSubscriptionUnSubscriptionConfirmation()
public ResponseEntity<Void> notification(HttpServletRequest httpServletRequest, @RequestBody() String requestBody) {
log.info("Notification payload: {}", requestBody);
String topicArn = httpServletRequest.getHeader("x-amz-sns-topic-arn");
String messageType = httpServletRequest.getHeader("x-amz-sns-message-type");
if ("Notification".equals(messageType)) {
JsonMapper mapper = JsonMapper.builder()
.configure(JsonReadFeature.ALLOW_UNQUOTED_FIELD_NAMES, true)
.build();
try {
Map<String, String> maps = mapper.readValue(requestBody, new TypeReference<Map<String, String>>() {
});
String message = maps.get("Message");
log.info("topic : {} message: {} ", topicArn, message);
} catch (JsonProcessingException e) {
log.error("topic : {} Notification failure error: {}", topicArn, e.getMessage());
} catch (HttpClientErrorException e) {
log.error("topic : {} Notification failure error: {}", topicArn, e.getResponseBodyAsString());
}
}
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
}
If you are using spring-cloud-aws, follow this awesome tutorial
Upvotes: 1