From 7779099a6c15508f2dd214a7555c66a27f8343ed Mon Sep 17 00:00:00 2001 From: Fredrik Tolf Date: Tue, 13 Oct 2009 03:21:07 +0200 Subject: [PATCH] Added a responder to serve static content from the classloader. --- build.xml | 5 +++ src/dolda/jsvc/j2ee/Archive.java | 15 ++------ src/dolda/jsvc/test/Bootstrap.java | 7 +++- src/dolda/jsvc/test/TestResponder.java | 32 +++++------------ src/dolda/jsvc/test/static/foo/a | 1 + src/dolda/jsvc/test/static/foo/b | 1 + src/dolda/jsvc/test/static/index.html | 12 +++++++ src/dolda/jsvc/test/static/test.css | 3 ++ src/dolda/jsvc/util/Http.java | 20 +++++++++++ src/dolda/jsvc/util/Misc.java | 20 +++++++++++ src/dolda/jsvc/util/Multiplexer.java | 15 ++------ src/dolda/jsvc/util/Restarts.java | 34 ++++++++++++++++++ src/dolda/jsvc/util/StaticContent.java | 66 ++++++++++++++++++++++++++++++++++ 13 files changed, 182 insertions(+), 49 deletions(-) create mode 100644 src/dolda/jsvc/test/static/foo/a create mode 100644 src/dolda/jsvc/test/static/foo/b create mode 100644 src/dolda/jsvc/test/static/index.html create mode 100644 src/dolda/jsvc/test/static/test.css create mode 100644 src/dolda/jsvc/util/Http.java create mode 100644 src/dolda/jsvc/util/StaticContent.java diff --git a/build.xml b/build.xml index c7a3065..4bd0d8d 100644 --- a/build.xml +++ b/build.xml @@ -30,6 +30,11 @@ + + + + + diff --git a/src/dolda/jsvc/j2ee/Archive.java b/src/dolda/jsvc/j2ee/Archive.java index b5893be..4a4dc01 100644 --- a/src/dolda/jsvc/j2ee/Archive.java +++ b/src/dolda/jsvc/j2ee/Archive.java @@ -1,5 +1,6 @@ package dolda.jsvc.j2ee; +import dolda.jsvc.util.*; import java.util.*; import java.io.*; import java.net.*; @@ -64,16 +65,6 @@ public class Archive { return(props); } - private static void cpstream(InputStream in, OutputStream out) throws IOException { - byte[] buf = new byte[4096]; - while(true) { - int ret = in.read(buf, 0, buf.length); - if(ret < 0) - return; - out.write(buf, 0, ret); - } - } - private static class MissingPropException extends RuntimeException { public final String prop; @@ -117,7 +108,7 @@ public class Archive { public void addcode(String name, InputStream in) throws IOException { zip().putNextEntry(new ZipEntry("WEB-INF/classes/" + name)); - cpstream(in, zip()); + Misc.cpstream(in, zip()); } public void addjars(File[] jars) throws IOException { @@ -127,7 +118,7 @@ public class Archive { zip.putNextEntry(new ZipEntry("WEB-INF/lib/" + jar.getName())); InputStream jarin = new FileInputStream(jar); try { - cpstream(jarin, zip); + Misc.cpstream(jarin, zip); } finally { jarin.close(); } diff --git a/src/dolda/jsvc/test/Bootstrap.java b/src/dolda/jsvc/test/Bootstrap.java index eb73996..ad4c38f 100644 --- a/src/dolda/jsvc/test/Bootstrap.java +++ b/src/dolda/jsvc/test/Bootstrap.java @@ -5,6 +5,11 @@ import dolda.jsvc.util.*; public class Bootstrap { public static Responder responder() { - return(new ErrorHandler(new Rehandler(new TestResponder()))); + Multiplexer root = new Multiplexer(); + root.file("test", new TestResponder()); + root.file("", new StaticContent(Bootstrap.class, "static/index.html", false, "text/html")); + root.file("css", new StaticContent(Bootstrap.class, "static/test.css", false, "text/css")); + root.dir("foo", new StaticContent(Bootstrap.class, "static/foo", true, "text/plain; charset=utf-8")); + return(Misc.stdroot(root)); } } diff --git a/src/dolda/jsvc/test/TestResponder.java b/src/dolda/jsvc/test/TestResponder.java index 3807a2a..e765d2e 100644 --- a/src/dolda/jsvc/test/TestResponder.java +++ b/src/dolda/jsvc/test/TestResponder.java @@ -4,32 +4,16 @@ import dolda.jsvc.*; import dolda.jsvc.util.*; import java.io.*; -public class TestResponder implements Responder { - public void respond(Request req) { - req.outheaders().put("Content-Type", "text/html; charset=utf-8"); - PrintWriter out; - try { - out = new PrintWriter(new OutputStreamWriter(req.output(), "UTF-8")); - } catch(UnsupportedEncodingException e) { - throw(new Error(e)); - } - - if(req.path().equals("bard1")) - throw(new RuntimeException("bard1")); - - out.println(""); - out.println("Barda"); - out.println(""); - out.println("

Barda

"); - out.println("Bardslen."); +public class TestResponder extends SimpleWriter { + public TestResponder() { + super("plain"); + } + + public void respond(Request req, PrintWriter out) { + out.println(req.url()); + out.println(req.path()); out.println(req.inheaders()); out.println(req.ctx().starttime()); out.println(req.remoteaddr() + "<->" + req.localaddr()); - out.println(""); - out.println(""); - - if(req.path().equals("bard2")) - throw(Restarts.redirectctx("/slen")); - out.flush(); } } diff --git a/src/dolda/jsvc/test/static/foo/a b/src/dolda/jsvc/test/static/foo/a new file mode 100644 index 0000000..6e1f517 --- /dev/null +++ b/src/dolda/jsvc/test/static/foo/a @@ -0,0 +1 @@ +Test A diff --git a/src/dolda/jsvc/test/static/foo/b b/src/dolda/jsvc/test/static/foo/b new file mode 100644 index 0000000..d4e395a --- /dev/null +++ b/src/dolda/jsvc/test/static/foo/b @@ -0,0 +1 @@ +Test B diff --git a/src/dolda/jsvc/test/static/index.html b/src/dolda/jsvc/test/static/index.html new file mode 100644 index 0000000..8553fc9 --- /dev/null +++ b/src/dolda/jsvc/test/static/index.html @@ -0,0 +1,12 @@ + + + + +Test + + + +

Hello world!

+

Test

+ + diff --git a/src/dolda/jsvc/test/static/test.css b/src/dolda/jsvc/test/static/test.css new file mode 100644 index 0000000..eb67f25 --- /dev/null +++ b/src/dolda/jsvc/test/static/test.css @@ -0,0 +1,3 @@ +body { + font-family: sans; +} diff --git a/src/dolda/jsvc/util/Http.java b/src/dolda/jsvc/util/Http.java new file mode 100644 index 0000000..000e224 --- /dev/null +++ b/src/dolda/jsvc/util/Http.java @@ -0,0 +1,20 @@ +package dolda.jsvc.util; + +import java.util.*; +import java.text.*; + +public class Http { + public final static DateFormat datefmt; + static { + datefmt = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH); + datefmt.setCalendar(Calendar.getInstance(TimeZone.getTimeZone("UTC"))); + } + + public static String fmtdate(Date d) { + return(datefmt.format(d)); + } + + public static Date parsedate(String str) throws ParseException { + return(datefmt.parse(str)); + } +} diff --git a/src/dolda/jsvc/util/Misc.java b/src/dolda/jsvc/util/Misc.java index cc01f1f..e92b324 100644 --- a/src/dolda/jsvc/util/Misc.java +++ b/src/dolda/jsvc/util/Misc.java @@ -1,6 +1,8 @@ package dolda.jsvc.util; +import dolda.jsvc.*; import java.util.*; +import java.io.*; public class Misc { private static Map stext = new HashMap(); @@ -11,6 +13,7 @@ public class Misc { stext.put(301, "Permanently Moved"); stext.put(302, "Temporarily Moved"); stext.put(303, "See Other"); + stext.put(304, "Not Modified"); stext.put(400, "Bad Request"); stext.put(401, "Authentication Required"); stext.put(403, "Access Forbidden"); @@ -32,4 +35,21 @@ public class Misc { p = p.substring(1); return(p); } + + public static void cpstream(InputStream in, OutputStream out) throws IOException { + byte[] buf = new byte[4096]; + while(true) { + int ret = in.read(buf, 0, buf.length); + if(ret < 0) + return; + out.write(buf, 0, ret); + } + } + + public static Responder stdroot(Responder root) { + Responder ret = root; + ret = new Rehandler(ret); + ret = new ErrorHandler(ret); + return(ret); + } } diff --git a/src/dolda/jsvc/util/Multiplexer.java b/src/dolda/jsvc/util/Multiplexer.java index dabcb34..df9e4c2 100644 --- a/src/dolda/jsvc/util/Multiplexer.java +++ b/src/dolda/jsvc/util/Multiplexer.java @@ -16,18 +16,9 @@ public class Multiplexer implements Responder { } public Multiplexer() { - this(new SimpleWriter("html") { - public void respond(Request req, java.io.PrintWriter out) { - req.status(404); - out.println(""); - out.println(""); - out.println(""); - out.println("Resource not found"); - out.println(""); - out.println("

Resource not found

"); - out.println("The resource you requested could not be found on this server."); - out.println(""); - out.println(""); + this(new Responder() { + public void respond(Request req) { + throw(Restarts.stdresponse(404, "Resource not found", "The resource you requested could not be found on this server.")); } }); } diff --git a/src/dolda/jsvc/util/Restarts.java b/src/dolda/jsvc/util/Restarts.java index 1953667..7725f28 100644 --- a/src/dolda/jsvc/util/Restarts.java +++ b/src/dolda/jsvc/util/Restarts.java @@ -2,6 +2,7 @@ package dolda.jsvc.util; import dolda.jsvc.*; import java.net.*; +import java.io.*; public class Restarts { public static RequestRestart redirect(final URL to) { @@ -43,4 +44,37 @@ public class Restarts { } }); } + + public static RequestRestart stdresponse(final int code, final String title, final String message) { + return(new RequestRestart() { + public void respond(Request req) { + req.status(code); + req.outheaders().put("content-type", "text/html; charset=us-ascii"); + PrintWriter out; + try { + out = new PrintWriter(new OutputStreamWriter(req.output(), "US-ASCII")); + } catch(UnsupportedEncodingException e) { + throw(new Error(e)); + } + out.println(""); + out.println(""); + out.println(""); + out.println("" + title + ""); + out.println(""); + out.println("

" + title + "

"); + out.println(message); + out.println(""); + out.println(""); + out.flush(); + } + }); + } + + public static RequestRestart stdresponse(int code, String message) { + return(stdresponse(code, "An error occurred", message)); + } + + public static RequestRestart stdresponse(int code) { + return(stdresponse(code, "An error occurred", Misc.statustext(code))); + } } diff --git a/src/dolda/jsvc/util/StaticContent.java b/src/dolda/jsvc/util/StaticContent.java new file mode 100644 index 0000000..0d4208e --- /dev/null +++ b/src/dolda/jsvc/util/StaticContent.java @@ -0,0 +1,66 @@ +package dolda.jsvc.util; + +import dolda.jsvc.*; +import java.io.*; +import java.util.*; + +public class StaticContent implements Responder { + private final Class base; + private final String resname; + private final boolean dir; + private final String mimetype; + + public StaticContent(Class base, String resname, boolean dir, String mimetype) { + this.base = base; + this.resname = resname; + this.dir = dir; + this.mimetype = mimetype; + } + + public StaticContent(String resname, boolean dir, String mimetype) { + this(null, resname, dir, mimetype); + } + + public void respond(Request req) { + String nm; + if(dir) { + nm = resname + "/" + req.path(); + } else { + nm = resname; + } + InputStream in; + if(base == null) { + in = StaticContent.class.getClassLoader().getResourceAsStream(nm); + } else { + in = base.getResourceAsStream(nm); + } + if(in == null) + throw(Restarts.stdresponse(404)); + String ims = req.inheaders().get("If-Modified-Since"); + Date mtime = new Date((req.ctx().starttime() / 1000) * 1000); + if(ims != null) { + Date d; + try { + d = Http.parsedate(ims); + } catch(java.text.ParseException e) { + throw(Restarts.stdresponse(400)); + } + if(mtime.compareTo(d) <= 0) { + req.status(304); + req.outheaders().put("Content-Length", "0"); + return; + } + } + try { + try { + req.outheaders().put("Content-Type", mimetype); + req.outheaders().put("Last-Modified", Http.fmtdate(mtime)); + Misc.cpstream(in, req.output()); + } finally { + in.close(); + } + } catch(IOException e) { + throw(new Error(e)); + } + } +} -- 2.11.0