package cn.aprilsoft.TinyAppServer.processor.impl;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.SocketException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.log4j.Logger;
import cn.aprilsoft.TinyAppServer.error.HttpErrorMessage;
import cn.aprilsoft.TinyAppServer.exception.ClientAbortException;
import cn.aprilsoft.TinyAppServer.exception.HttpException;
import cn.aprilsoft.TinyAppServer.processor.Processor;
import cn.aprilsoft.TinyAppServer.reqNrep.Request;
import cn.aprilsoft.TinyAppServer.reqNrep.Response;
import cn.aprilsoft.conf4j.Conf4j;
import cn.aprilsoft.io.FileUtil;
import cn.aprilsoft.io.IOUtil;
import cn.aprilsoft.mime.ContentType;
public class FileServerProcessor extends Processor {
private static final Logger log = Logger.getLogger(FileServerProcessor.class);
private static final Pattern P = Pattern.compile("([^:@]+):([^:@]+)(?:@([^:@]+))?");
private Map<String, Setting> settingMap = new LinkedHashMap<String, Setting>();
public static interface UrlToPath {
String toPath(String url);
}
public static class Setting {
private String prefix;
private String path;
private UrlToPath clazz;
public String getPrefix() {
return prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public UrlToPath getClazz() {
return clazz;
}
public void setClazz(UrlToPath clazz) {
this.clazz = clazz;
}
@Override
public String toString() {
return "Setting [prefix=" + prefix + ", path=" + path + ", clazz=" + clazz + "]";
}
}
public FileServerProcessor() {
String setting = Conf4j.getProperty(FileServerProcessor.class, "setting");
if (setting != null) {
String[] pairs = setting.split(";");
for (String p : pairs) {
Matcher m = P.matcher(p);
if (m.matches()) {
Setting set = new Setting();
set.setPrefix(m.group(1).trim());
set.setPath(m.group(2).trim());
try {
if (m.group(3) != null) {
set.setClazz((UrlToPath) Class.forName(m.group(3).trim()).newInstance());
}
} catch (Exception e) {
throw new RuntimeException(e);
}
settingMap.put(set.getPrefix(), set);
log.info("Setting map:" + settingMap.values());
}
}
}
}
@Override
protected void doGet(Request request, Response response) {
doPost(request, response);
}
@Override
protected void doPost(Request request, Response response) {
String url = request.getUrl();
boolean hit = false;
for (String k : settingMap.keySet()) {
if (url.startsWith(k)) {
hit = true;
Setting set = settingMap.get(k);
String subUrl = url.substring(k.length());
String subPath = (set.getClazz() == null) ? subUrl : set.getClazz().toPath(subUrl);
String path = FileUtil.combilePath(set.getPath(), subPath);
File file = new File(path);
if (file.exists() && file.isFile()) {
if ((!file.canRead()) || file.isHidden()) {
log.warn("File '" + file + "' access denied.");
HttpErrorMessage.error403(response);
} else {
try {
outputFileToResponse(response, file);
} catch (SocketException e) {
throw new ClientAbortException(e);
} catch (IOException e) {
throw new HttpException(e);
}
}
} else {
log.warn("File '" + file + "' not exist.");
HttpErrorMessage.error404(response);
}
}
}
if (!hit) {
log.warn("File '" + url + "' not exist.");
HttpErrorMessage.error404(response);
}
}
private void outputFileToResponse(Response response, File file) throws IOException {
String fileName = file.getName();
int lastDot = fileName.lastIndexOf('.');
String ext = null;
if (lastDot >= 0) {
ext = fileName.substring(lastDot + 1);
}
response.setContentType(ContentType.getContentTypeByExt(ext));
FileInputStream fis = new FileInputStream(file);
response.setContentSize(fis.available());
OutputStream os = response.getOutputStream();
try {
IOUtil.copy(fis, os);
} finally {
fis.close();
}
os.flush();
}
}