package cn.webwheel.whtml;
import cn.webwheel.template.FileVisitor;
import cn.webwheel.template.RendererFactory;
import cn.webwheel.template.TemplateRenderer;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import java.io.*;
import java.net.URLDecoder;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class WHandler implements HttpHandler {
private final File root;
private String uriEncoding;
private Map<String, String> mimeMap = new HashMap<String, String>();
private ObjectMapper mapper = new ObjectMapper();
private RendererFactory rendererFactory;
public WHandler(File root, String uriEncoding) throws IOException {
this.root = root;
this.uriEncoding = uriEncoding;
rendererFactory = new RendererFactory(root, null, mapper);
mapper.getJsonFactory().enable(JsonParser.Feature.ALLOW_SINGLE_QUOTES);
mapper.getJsonFactory().enable(JsonParser.Feature.ALLOW_COMMENTS);
mapper.getJsonFactory().enable(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES);
BufferedReader reader = new BufferedReader(new InputStreamReader(WHandler.class.getResourceAsStream("/res/mime.txt"), "utf-8"));
try {
String line;
while ((line = reader.readLine()) != null) {
mimeMap.put(line, reader.readLine());
}
} finally {
reader.close();
}
}
private String getWelcome(String path) {
if (new File(root, path + "index.html").exists()) {
return path + "index.html";
}
return null;
}
private void readLine(InputStream is, ByteArrayOutputStream baos) throws IOException {
baos.reset();
boolean preisr = false;
int d;
while ((d = is.read()) != -1) {
if (d == '\n' && preisr) {
return;
}
if (preisr) {
baos.write('\r');
}
if (!(preisr = d == '\r')) {
baos.write(d);
}
}
if (preisr) {
baos.write('\r');
}
}
private int boundaryEqual(String boundary, ByteArrayOutputStream baos) throws IOException {
if (boundary.length() + 2 == baos.size()) {
if (("--" + boundary).equals(new String(baos.toByteArray(), "iso8859-1"))) {
return 1;
}
} else if (boundary.length() + 4 == baos.size()) {
if (("--" + boundary + "--").equals(new String(baos.toByteArray(), "iso8859-1"))) {
return 2;
}
}
return 0;
}
private Map<String, Object> parse(HttpExchange httpExchange) throws IOException {
String contentType = httpExchange.getRequestHeaders().getFirst("Content-Type");
HashMap<String, Object> map = new HashMap<String, Object>();
if (httpExchange.getRequestMethod().equalsIgnoreCase("post") && contentType != null) {
if (contentType.equals("application/x-www-form-urlencoded")) {
int len = Integer.parseInt(httpExchange.getRequestHeaders().getFirst("Content-Length"));
byte[] buf = new byte[len];
DataInputStream dis = new DataInputStream(httpExchange.getRequestBody());
dis.readFully(buf);
parse(map, new String(buf, "iso8859-1"));
} else if (contentType.startsWith("multipart/form-data; boundary=")) {
String boundary = contentType.substring("multipart/form-data; boundary=".length());
BufferedInputStream is = new BufferedInputStream(httpExchange.getRequestBody());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
readLine(is, baos);
int r = boundaryEqual(boundary, baos);
if (r != 1) return map;
loop:
while (true) {
String name = null;
String filename = null;
while (true){
readLine(is, baos);
if (baos.size() == 0) break;
String s = new String(baos.toByteArray(), "iso8859-1");
if (s.startsWith("Content-Disposition:")) {
for (String ss : s.split(";")) {
ss = ss.trim();
if (ss.startsWith("name=")) {
name = ss.substring("name=".length() + 1, ss.length() - 1);
} else if (ss.startsWith("filename=")) {
filename = ss.substring("filename=".length() + 1, ss.length() - 1);
}
}
}
}
ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
while (true) {
readLine(is, baos);
r = boundaryEqual(boundary, baos);
if (r == 0) {
baos2.write(baos.toByteArray());
continue;
}
if (name != null) {
if (filename != null) {
Object files = map.get(name);
if (files == null) {
map.put(name, files = new File[0]);
}
if (files instanceof File[]) {
File[] fs = (File[]) files;
File[] nfs = new File[fs.length + 1];
System.arraycopy(fs, 0, nfs, 0, fs.length);
nfs[fs.length] = new File(filename);
map.put(name, nfs);
}
} else {
Object vals = map.get(name);
if (vals == null) {
map.put(name, vals = new String[0]);
}
if (vals instanceof String[]) {
String[] vs = (String[]) vals;
String[] nvs = new String[vs.length + 1];
System.arraycopy(vs, 0, nvs, 0, vs.length);
nvs[vs.length] = new String(baos2.toByteArray(), uriEncoding);
map.put(name, nvs);
}
}
}
if (r == 1) {
continue loop;
} else {
break loop;
}
}
}
}
}
return map;
}
private void parse(Map<String, Object> map, String query) throws UnsupportedEncodingException {
if (query == null) return;
query = query.trim().replace("&", "&");
String[] ss = query.split("&");
for (String s : ss) {
String[] sss = s.split("=");
if (sss.length == 2) {
sss[0] = URLDecoder.decode(sss[0], uriEncoding);
sss[1] = URLDecoder.decode(sss[1], uriEncoding);
Object o = map.get(sss[0]);
if (o == null) {
map.put(sss[0], new String[]{sss[1]});
} else if (o instanceof String[]) {
String[] vs = (String[]) o;
String[] nvs = new String[vs.length + 1];
System.arraycopy(vs, 0, nvs, 0, vs.length);
nvs[vs.length] = sss[1];
map.put(sss[0], nvs);
}
}
}
}
private void send404(HttpExchange httpExchange) throws IOException {
byte[] msg = "Page NotFound".getBytes("utf-8");
httpExchange.getResponseHeaders().add("Content-Type", "text/plain; charset=utf-8");
httpExchange.sendResponseHeaders(404, msg.length);
httpExchange.getResponseBody().write(msg);
httpExchange.close();
}
private void sendMime(String path, String contentType, String charset, HttpExchange httpExchange) {
String mime = contentType;
if (mime == null) {
int i = path.lastIndexOf('.');
if (i != -1) {
mime = mimeMap.get(path.substring(i + 1));
}
}
if (mime == null) return;
if (mime.startsWith("text") || charset != null) {
httpExchange.getResponseHeaders().add("Content-Type", mime + "; charset=" + charset);
} else {
httpExchange.getResponseHeaders().add("Content-Type", mime);
}
}
private void def(String path, HttpExchange httpExchange) throws IOException {
File file = new File(root, path);
if (!file.exists()) {
send404(httpExchange);
return;
}
byte[] data = new byte[(int) file.length()];
DataInputStream dis = new DataInputStream(new FileInputStream(file));
try {
dis.readFully(data);
} catch (IOException e) {
except(httpExchange, e);
return;
} finally {
dis.close();
}
sendMime(path, null, null, httpExchange);
httpExchange.sendResponseHeaders(200, data.length);
httpExchange.getResponseBody().write(data);
httpExchange.close();
}
private void except(HttpExchange httpExchange, Throwable e) throws IOException {
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
except(httpExchange, sw.toString());
}
private void except(HttpExchange httpExchange, String e) throws IOException {
httpExchange.getResponseHeaders().add("Content-Type", "text/plain; charset=utf-8");
byte[] data = e.getBytes("utf-8");
httpExchange.sendResponseHeaders(200, data.length);
httpExchange.getResponseBody().write(data);
httpExchange.close();
}
private void handle(HttpExchange httpExchange, String path, String query, Map<String, Object> params) throws IOException {
if (path.toLowerCase().endsWith(".html") || path.toLowerCase().endsWith(".htm")) {
TemplateRenderer renderer;
StringWriter sw = new StringWriter();
String rCharset;
try {
renderer = rendererFactory.create(path, null, null);
rCharset = renderer.charset == null ? "utf-8" : renderer.charset;
StringBuilder sb = new StringBuilder();
File file = new File(root, path);
char[] buf = new char[8192];
int rd;
InputStreamReader reader = new InputStreamReader(new FileInputStream(file), rCharset);
try {
while ((rd = reader.read(buf)) != -1) {
sb.append(buf, 0, rd);
}
} finally {
reader.close();
}
Map<String, Object> ctx = null;
Pattern pat = Pattern.compile("<script.*?>\\s*var _model\\s*=\\s*(.+?);?\\s*</script>",
Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
Matcher matcher = pat.matcher(sb.toString());
if (matcher.find()) {
Map<String, Object> map = mapper.readValue(matcher.group(1), Map.class);
if (map.size() == 1) {
ctx = (Map) map.entrySet().iterator().next().getValue();
} else if (map.size() > 1) {
String mi = null;
try {
Object o = params.get("_model");
if (o instanceof String[]) {
mi = ((String[]) o)[0];
}
} catch (Exception ignored) {
}
ctx = (Map) map.get(mi);
if (ctx == null) {
ModelSelectDlg dlg = new ModelSelectDlg(path, map.keySet());
dlg.setVisible(true);
if (dlg.selected != null) {
ctx = (Map) map.get(dlg.selected);
}
}
}
}
if (ctx == null) ctx = Collections.emptyMap();
renderer.render(sw, ctx);
} catch (Exception e) {
except(httpExchange, e);
return;
}
byte[] data = sw.toString().getBytes(rCharset);
sendMime(path, renderer.contentType, rCharset, httpExchange);
httpExchange.sendResponseHeaders(200, data.length);
httpExchange.getResponseBody().write(data);
httpExchange.close();
return;
}
File file = new File(root, path);
if (file.exists() && file.length() < 1024 * 1024) {
Map<String, Object> map = null;
Reader reader = null;
try {
reader = new InputStreamReader(new FileInputStream(file), "utf-8");
map = mapper.readValue(reader, Map.class);
} catch (IOException ignored) {
}
if (reader != null) reader.close();
if (map != null) {
Object obj = null;
if (map.size() == 1) {
obj = map.values().iterator().next();
} else if (map.size() > 1) {
ModelSelectDlg dlg = new ModelSelectDlg(path, map.keySet());
dlg.setVisible(true);
if (dlg.selected != null) {
obj = map.get(dlg.selected);
}
}
if (obj != null) {
byte[] data = null;
try {
data = mapper.writeValueAsBytes(obj);
} catch (IOException ignored) {
}
if (data != null) {
sendMime(".json", null, "utf-8", httpExchange);
httpExchange.sendResponseHeaders(200, data.length);
httpExchange.getResponseBody().write(data);
httpExchange.close();
return;
}
}
}
}
def(path, httpExchange);
}
private void renderResource(HttpExchange httpExchange, String resource, Map<String, Object> ctx) throws Exception {
byte[] data;
if (ctx != null) {
TemplateRenderer renderer = rendererFactory.create(resource, new FileVisitor() {
@Override
public String read(File root, String file, String charset) throws IOException {
InputStreamReader reader = new InputStreamReader(getClass().getResourceAsStream(file), "utf-8");
try {
StringBuilder sb = new StringBuilder();
int rd;
char[] buf = new char[4096];
while ((rd = reader.read(buf)) != -1) {
sb.append(buf, 0, rd);
}
return sb.toString();
} finally {
reader.close();
}
}
}, null);
StringWriter sw = new StringWriter();
try {
renderer.render(sw, ctx);
} catch (IOException e) {
throw e;
} catch (Exception e) {
throw new IOException(e);
}
data = sw.toString().getBytes("utf-8");
} else {
InputStream is = getClass().getResourceAsStream("res/" + resource);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[8192];
int rd;
while ((rd = is.read(buf)) != -1) {
baos.write(buf, 0, rd);
}
data = baos.toByteArray();
}
sendMime(resource, null, "utf-8", httpExchange);
httpExchange.sendResponseHeaders(200, data.length);
httpExchange.getResponseBody().write(data);
httpExchange.close();
}
private void listDir(String path, HttpExchange httpExchange) throws Exception {
Map<String, Object> map = new HashMap<String, Object>();
map.put("isRoot", path.equals("/"));
if (!path.equals("/")) {
map.put("parentPath", path.substring(0, path.lastIndexOf('/', path.length() - 2) + 1));
}
File dir = new File(root, path);
map.put("dir", dir);
List<File> files = new ArrayList<File>();
File[] list = dir.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.isDirectory();
}
});
if (list != null) files.addAll(Arrays.asList(list));
list = dir.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.isFile();
}
});
if (list != null) files.addAll(Arrays.asList(list));
map.put("files", files);
renderResource(httpExchange, "/res/dir.html", map);
}
@Override
public void handle(HttpExchange httpExchange) throws IOException {
String path = httpExchange.getRequestURI().getPath();
if (path.endsWith("/")) {
String welcome = getWelcome(path);
if (welcome == null) {
try {
listDir(path, httpExchange);
} catch (Exception e) {
except(httpExchange, e);
}
return;
}
path = welcome;
}
String query = httpExchange.getRequestURI().getQuery();
Map<String, Object> params = parse(httpExchange);
parse(params, query);
handle(httpExchange, path, query, params);
}
}