package com.headissue.sharecount.proxy; import com.headissue.sharecount.provider.Delicious; import com.headissue.sharecount.provider.Facebook; import com.headissue.sharecount.provider.GooglePlus; import com.headissue.sharecount.provider.LinkedIn; import com.headissue.sharecount.provider.Pinterest; import com.headissue.sharecount.provider.Reddit; import com.headissue.sharecount.provider.ShareCountProvider; import com.headissue.sharecount.provider.StumbleUpon; import com.headissue.sharecount.provider.Twitter; import javax.servlet.*; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.net.URLEncoder; import java.text.SimpleDateFormat; import java.util.Date; import java.util.regex.Matcher; import java.util.regex.Pattern; public class ShareCountProxy extends HttpServlet { private Config config; private ShareCountProvider[] countProvider = new ShareCountProvider[]{ new Reddit(), new Facebook(), new Twitter(), new LinkedIn(), new Delicious(), new StumbleUpon(), new Pinterest(), new GooglePlus() }; public ShareCountProxy() { config = ConfigBuilder.buildFromEnv(); } public void setConfig(Config config) { this.config = config; } @Override protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { addCacheHeaders(resp); resp.addHeader("allow", "GET"); resp.addHeader("Access-Control-Allow-Origin", "*"); resp.setStatus(200); resp.setContentLength(0); } protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String forUrl = req.getParameter("url"); if (forUrl == null) { // access to root will displays a welcome page Writer out = resp.getWriter(); out.write("Hello!\nshariff-backend-java is running.\n" + "Request the sharecounts like: /?url=http://example.com\n\n" + "~~~\nhttps://github.com/headissue/shariff-backend-java"); return; } else { try { validateUrl(forUrl); } catch (ValidationException e) { resp.sendError(400, e.getMessage()); return; } String json = getCounts(forUrl); PrintWriter out = resp.getWriter(); addCacheHeaders(resp); resp.addHeader("Content-Type", "application/json"); resp.addHeader("Access-Control-Allow-Origin", "*"); resp.addHeader("Access-Control-Expose-Headers", "Content-Type"); out.print(json); } } /** * compares the url with a list of patterns from the config * throws an exception if the url is not whitelisted * @param forUrl * @throws ValidationException */ protected void validateUrl(String forUrl) throws ValidationException { String domainWhiteListValue = config.getDomainList(); String[] domains = new String[0]; if (domainWhiteListValue != null) { domains = domainWhiteListValue.split(";"); } for (int i = 0; i < domains.length; i++) { String domain = domains[i]; Pattern p = Pattern.compile(domain); Matcher m = p.matcher(forUrl); if (m.matches()) { // this url is allowed to be requested sharecounts for return; } } // nothing matches throw new ValidationException("Disallowed query."); } private void addCacheHeaders(HttpServletResponse resp) { int fortySevenSeconds = 1000 * 47; resp.addHeader("Cache-Control", "max-age=" + fortySevenSeconds); SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z"); resp.addHeader("Expires", dateFormat.format(new Date(new Date().getTime() + fortySevenSeconds))); } protected String getCounts(String forUrl) { if (!forUrl.startsWith("http") || !forUrl.contains("://")) { forUrl = "http://" + forUrl; } try { forUrl = URLEncoder.encode(forUrl, "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } // signals the cache that we need an item soon for (ShareCountProvider p : countProvider) { p.prefetch(forUrl); } StringBuilder sb = new StringBuilder(); sb.append("{"); for (int i = 0; i < countProvider.length; i++) { ShareCountProvider provider = countProvider[i]; String name = provider.getName(); int count = provider.getCount(forUrl); sb.append("\"").append(name).append("\"").append(":").append(count); if ((i + 1) < countProvider.length) { sb.append(","); } } sb.append("}"); return sb.toString(); } protected class ValidationException extends Exception { public ValidationException(String s) { super(s); } } }