<target name="build-env">
<mkdir dir="build" />
<mkdir dir="build/bin" />
- <mkdir dir="build/test-bin" />
<mkdir dir="build/api" />
</target>
<zip destfile="build/jsvc-api.zip" basedir="build/api" />
</target>
- <target name="test-war" depends="build-env, jsvc-jar">
- <taskdef name="jsvc-war" classname="dolda.jsvc.j2ee.Archive$AntTask" classpath="build/jsvc.jar" />
- <copy todir="build/test-bin/dolda/jsvc/test">
- <fileset dir="src/dolda/jsvc/test">
- <include name="static/**" />
- </fileset>
- </copy>
- <copy tofile="build/test-bin/jsvc.properties" file="etc/test.jsvc.properties" />
- <jar destfile="build/jsvc-test.jar" basedir="build/test-bin" />
- <jsvc-war destfile="build/jsvc-test.war">
- <jars dir="build">
- <include name="jsvc.jar" />
- <include name="jsvc-test.jar" />
- </jars>
- </jsvc-war>
- </target>
-
<target name="clean">
<delete dir="build" />
</target>
+++ /dev/null
-jsvc.j2ee.appname = JSvc Test War
-jsvc.bootstrap = dolda.jsvc.test.Bootstrap
+++ /dev/null
-package dolda.jsvc.test;
-
-import dolda.jsvc.*;
-import dolda.jsvc.util.*;
-
-public class Bootstrap {
- public static Responder responder() {
- 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));
- }
-}
+++ /dev/null
-package dolda.jsvc.test;
-
-import dolda.jsvc.*;
-import dolda.jsvc.util.*;
-import java.io.*;
-
-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());
- }
-}
+++ /dev/null
-<?xml version="1.0" encoding="US-ASCII"?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
-<head>
-<title>Test</title>
-<link rel="stylesheet" title="Default style" type="text/css" href="css" />
-</head>
-<body>
-<h1>Hello world!</h1>
-<p>Test</p>
-</body>
-</html>
+++ /dev/null
-body {
- font-family: sans;
-}
--- /dev/null
+package dolda.jsvc.util;
+
+import dolda.jsvc.*;
+import java.util.*;
+
+public class Cache {
+ public static void checkmtime(Request req, long mtime) {
+ /* Since the HTTP time format is (reasonably enough) precise
+ * only to seconds, any extra milliseconds must be trimmed
+ * off, or the mtime will almost certainly not match. */
+ Date mdate = new Date((mtime / 1000) * 1000);
+ String ims = req.inheaders().get("If-Modified-Since");
+ if(ims != null) {
+ Date cldate;
+ try {
+ cldate = Http.parsedate(ims);
+ } catch(java.text.ParseException e) {
+ throw(Restarts.stdresponse(400, "The If-Modified-Since header is not parseable."));
+ }
+ if(mdate.compareTo(cldate) <= 0) {
+ req.status(304);
+ req.outheaders().put("Content-Length", "0");
+ throw(Restarts.done());
+ }
+ }
+ req.outheaders().put("Last-Modified", Http.fmtdate(mdate));
+ }
+}
public class Multiplexer implements Responder {
private Responder def;
- private Collection<Sub> subs = new LinkedList<Sub>();
+ private Collection<Matcher> matchers = new LinkedList<Matcher>();
- private static interface Sub {
- boolean match(Request req);
+ public static interface Matcher {
+ public boolean match(Request req);
}
public Multiplexer(Responder def) {
}
public void file(final String path, final Responder responder) {
- subs.add(new Sub() {
+ add(new Matcher() {
public boolean match(Request req) {
if(req.path().equals(path)) {
responder.respond(req);
public void dir(String path, final Responder responder) {
final String fp = Misc.stripslashes(path, true, true);
- subs.add(new Sub() {
+ add(new Matcher() {
public boolean match(Request req) {
if(req.path().equals(fp)) {
throw(Restarts.redirect(fp + "/"));
});
}
+ public void add(Matcher m) {
+ matchers.add(m);
+ }
+
public void respond(Request req) {
- for(Sub s : subs) {
- if(s.match(req))
+ for(Matcher m : matchers) {
+ if(m.match(req))
return;
}
def.respond(req);
public static RequestRestart stdresponse(int code) {
return(stdresponse(code, "An error occurred", Misc.statustext(code)));
}
+
+ public static RequestRestart done() {
+ return(new RequestRestart() {
+ public void respond(Request req) {
+ }
+ });
+ }
}
}
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;
- }
- }
+ Cache.checkmtime(req, req.ctx().starttime());
try {
try {
req.outheaders().put("Content-Type", mimetype);
- req.outheaders().put("Last-Modified", Http.fmtdate(mtime));
Misc.cpstream(in, req.output());
} finally {
in.close();