package org.springframework.integration.aws.sns.core;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.PublicKey;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.web.HttpRequestHandler;
import org.springframework.web.bind.annotation.RequestMethod;
import com.amazonaws.services.sns.util.SignatureChecker;
public class HttpEndpoint implements HttpRequestHandler {
private static final String SUBSCRIPTION_CONFIRMATION = "SubscriptionConfirmation";
private static final String NOTIFICATION = "Notification";
private static final String SNS_MESSAGE_TYPE = "x-amz-sns-message-type";
private final Log log = LogFactory.getLog(HttpEndpoint.class);
private boolean passThru;
private volatile NotificationHandler notificationHandler;
public HttpEndpoint(NotificationHandler notificationHandler) {
this();
this.notificationHandler = notificationHandler;
}
public HttpEndpoint() {
super();
this.passThru = false;
}
public void setNotificationHandler(NotificationHandler notificationHandler) {
this.notificationHandler = notificationHandler;
}
public void setPassThru(boolean passThru) {
this.passThru = passThru;
}
@Override
public void handleRequest(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
boolean unprocessable = true;
if (request.getMethod().equals(RequestMethod.POST.toString())) {
if (SUBSCRIPTION_CONFIRMATION.equals(request
.getHeader(SNS_MESSAGE_TYPE))) {
unprocessable = false;
handleSubscriptionConfirmation(request, response);
} else if (NOTIFICATION.equals(request.getHeader(SNS_MESSAGE_TYPE))) {
if (notificationHandler != null) {
unprocessable = false;
handleNotification(request, response);
}
}
}
if (unprocessable) {
log.warn("Unprocessable request: "
+ request.getRequestURL().toString());
response.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);
}
}
private void handleSubscriptionConfirmation(HttpServletRequest request,
HttpServletResponse response) throws IOException {
try {
String source = readBody(request);
log.debug("Subscription confirmation:\n" + source);
JSONObject confirmation = new JSONObject(source);
if (validSignature(source, confirmation)) {
URL subscribeUrl = new URL(
confirmation.getString("SubscribeURL"));
HttpURLConnection http = (HttpURLConnection) subscribeUrl
.openConnection();
http.setDoOutput(false);
http.setDoInput(true);
StringBuilder buffer = new StringBuilder();
byte[] buf = new byte[4096];
while ((http.getInputStream().read(buf)) >= 0) {
buffer.append(new String(buf));
}
log.debug("SubscribeURL response:\n" + buffer.toString());
}
response.setStatus(HttpServletResponse.SC_OK);
} catch (JSONException e) {
throw new IOException(e.getMessage(), e);
}
}
private void handleNotification(HttpServletRequest request,
HttpServletResponse response) throws IOException {
try {
String source = readBody(request);
log.debug("Message received:\n" + source);
JSONObject notification = new JSONObject(source);
if (passThru || validSignature(source, notification)) {
notificationHandler.onNotification(notification
.getString("Message"));
}
response.setStatus(HttpServletResponse.SC_ACCEPTED);
} catch (JSONException e) {
throw new IOException(e.getMessage(), e);
}
}
private String readBody(HttpServletRequest request) throws IOException {
StringBuilder buffer = new StringBuilder(request.getContentLength());
String line = null;
BufferedReader reader = request.getReader();
while ((line = reader.readLine()) != null) {
buffer.append(line);
}
return buffer.toString();
}
private boolean validSignature(String source, JSONObject jsonObject)
throws JSONException, IOException {
PublicKey pubKey = fetchPubKey(jsonObject.getString("SigningCertURL"));
SignatureChecker signatureChecker = new SignatureChecker();
return signatureChecker.verifyMessageSignature(source, pubKey);
}
private PublicKey fetchPubKey(String signingCertURL) throws IOException {
try {
URL url = new URL(signingCertURL);
InputStream inStream = url.openStream();
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) cf
.generateCertificate(inStream);
inStream.close();
return cert.getPublicKey();
} catch (Exception e) {
throw new IOException(e);
}
}
}