package tv.dyndns.kishibe.qmaclone.server;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.http.impl.cookie.DateUtils;
import tv.dyndns.kishibe.qmaclone.server.image.ImageUtils;
import tv.dyndns.kishibe.qmaclone.server.image.ImageUtils.Parameter;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.inject.Inject;
@SuppressWarnings("serial")
public class ImageProxyServletStub extends HttpServlet {
private static final Logger logger = Logger.getLogger(ImageProxyServletStub.class.toString());
private static final int MAX_WIDTH = 512;
private static final int MAX_HEIGHT = 384;
private static String HEX = "0123456789abcdef";
private final ImageUtils imageUtils;
@Inject
public ImageProxyServletStub(ImageUtils imageUtils) {
this.imageUtils = imageUtils;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
try (InputStream inputStream = req.getInputStream();
OutputStream outputStream = resp.getOutputStream()) {
processGet(req, resp);
}
}
/**
* doGet()の内容を処理する
*
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
private void processGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
Parameter parameter = parseParameter(req);
resp.setContentType("image/jpeg");
resp.setHeader("Cache-Control", "max-age=31536000");
resp.setHeader("Expires", getRFC1123NextYear());
imageUtils.writeToStream(parameter, resp.getOutputStream());
}
@VisibleForTesting
Parameter parseParameter(HttpServletRequest req) throws ServletException {
Map<String, String> parameters = Maps.newHashMap();
String requestURI = req.getRequestURI();
List<String> segments = Lists.newArrayList(Splitter.on('/').omitEmptyStrings()
.split(requestURI));
while (!segments.get(0).equals("image")) {
segments.remove(0);
}
segments.remove(0);
for (int i = 0; i < segments.size() / 2; ++i) {
parameters.put(segments.get(i * 2), segments.get(i * 2 + 1));
}
String url = parameters.get("url");
String widthString = parameters.get("width");
String heightString = parameters.get("height");
String keepAspectRatioString = parameters.get("keepAspectRatio");
if (url == null || heightString == null || widthString == null) {
throw new ServletException("パラメーターが指定されていません");
}
try {
url = decode(url);
} catch (RuntimeException e) {
throw new ServletException("URL部のデコードに失敗しました", e);
}
int width = 0;
try {
width = Integer.parseInt(widthString);
} catch (Exception e) {
throw new ServletException("幅パラメーターがintとして解釈できません", e);
}
if (width > MAX_WIDTH) {
throw new ServletException("幅パラメーターの値が大きすぎます " + width);
}
if (width <= 0) {
throw new ServletException("幅パラメーターの値が小さすぎます " + width);
}
int height = 0;
try {
height = Integer.parseInt(heightString);
} catch (Exception e) {
throw new ServletException("高さパラメーターがintとして解釈できません", e);
}
if (height > MAX_HEIGHT) {
throw new ServletException("高さパラメーターの値が大きすぎます " + height);
}
if (height <= 0) {
throw new ServletException("高さパラメーターの値が小さすぎます " + height);
}
boolean keepAspectRatio = false;
try {
keepAspectRatio = Boolean.parseBoolean(keepAspectRatioString);
} catch (Exception e) {
throw new ServletException("アクペクト比パラメーターがbooleanとして解釈できません", e);
}
return new Parameter(url, width, height, keepAspectRatio);
}
private String decode(String s) {
StringBuilder sb = new StringBuilder();
int index = 0;
while (index < s.length()) {
int ch = 0;
for (int i = 0; i < 4; ++i) {
ch <<= 4;
ch |= HEX.indexOf(s.charAt(index++));
}
sb.append((char) ch);
}
return sb.toString();
}
@Override
protected long getLastModified(HttpServletRequest req) {
long result = 0;
try {
result = imageUtils.getLastModified(parseParameter(req));
} catch (ServletException e) {
logger.log(Level.WARNING, "LastModifiedの取得に失敗しました", e);
}
if (result == 0) {
result = super.getLastModified(req);
}
return result;
}
/**
* 一年後の日時をRFC1123形式で返す
*
* @return
*/
String getRFC1123NextYear() {
final Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.YEAR, 1);
return DateUtils.formatDate(calendar.getTime());
}
}