Author: toad
Date: 2006-01-14 01:51:07 +0000 (Sat, 14 Jan 2006)
New Revision: 7853
Added:
trunk/freenet/src/freenet/clients/
trunk/freenet/src/freenet/clients/http/
trunk/freenet/src/freenet/clients/http/FproxyToadlet.java
trunk/freenet/src/freenet/clients/http/SimpleToadletServer.java
trunk/freenet/src/freenet/clients/http/Toadlet.java
trunk/freenet/src/freenet/clients/http/ToadletContainer.java
trunk/freenet/src/freenet/clients/http/ToadletContext.java
trunk/freenet/src/freenet/clients/http/ToadletContextClosedException.java
trunk/freenet/src/freenet/clients/http/ToadletContextImpl.java
trunk/freenet/src/freenet/clients/http/TrivialToadlet.java
trunk/freenet/src/freenet/support/HTMLDecoder.java
trunk/freenet/src/freenet/support/HTMLEncoder.java
trunk/freenet/src/freenet/support/LimitedEnumeration.java
trunk/freenet/src/freenet/support/MultiValueTable.java
trunk/freenet/src/freenet/support/URLDecoder.java
trunk/freenet/src/freenet/support/URLEncodedFormatException.java
trunk/freenet/src/freenet/support/URLEncoder.java
trunk/freenet/src/freenet/support/io/LineReadingInputStream.java
trunk/freenet/src/freenet/support/io/TooLongException.java
Modified:
trunk/freenet/src/freenet/node/Node.java
trunk/freenet/src/freenet/node/Version.java
Log:
349: Working fproxy! Of course it doesn't filter yet, and the GUI isn't very
good.
Added: trunk/freenet/src/freenet/clients/http/FproxyToadlet.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/FproxyToadlet.java 2006-01-13
18:09:01 UTC (rev 7852)
+++ trunk/freenet/src/freenet/clients/http/FproxyToadlet.java 2006-01-14
01:51:07 UTC (rev 7853)
@@ -0,0 +1,62 @@
+package freenet.clients.http;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.MalformedURLException;
+import java.net.URI;
+
+import freenet.client.FetchException;
+import freenet.client.FetchResult;
+import freenet.client.HighLevelSimpleClient;
+import freenet.keys.FreenetURI;
+import freenet.support.Bucket;
+import freenet.support.HTMLEncoder;
+import freenet.support.Logger;
+
+public class FproxyToadlet extends Toadlet {
+
+ public FproxyToadlet(HighLevelSimpleClient client) {
+ super(client);
+ }
+
+ void handleGet(URI uri, ToadletContext ctx)
+ throws ToadletContextClosedException, IOException {
+ String ks = uri.toString();
+ if(ks.startsWith("/"))
+ ks = ks.substring(1);
+ FreenetURI key;
+ try {
+ key = new FreenetURI(ks);
+ } catch (MalformedURLException e) {
+ this.writeReply(ctx, 400, "text/html", "Invalid key",
"<html><head><title>Invalid key</title></head><body>Expected a freenet key, but
got "+HTMLEncoder.encode(ks)+"</body></html>");
+ return;
+ }
+ try {
+ FetchResult result = fetch(key);
+ writeReply(ctx, 200, result.getMimeType(), "OK",
result.asBucket());
+ } catch (FetchException e) {
+ String msg = e.getMessage();
+ this.writeReply(ctx, 500 /* close enough - FIXME
probably should depend on status code */,
+ "text/html", msg,
"<html><head><title>"+msg+"</title></head><body>Error:
"+HTMLEncoder.encode(msg)+"</body></html>");
+ } catch (Throwable t) {
+ Logger.error(this, "Caught "+t, t);
+ String msg = "<html><head><title>Internal
Error</title></head><body><h1>Internal Error: please report</h1><pre>";
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ t.printStackTrace(pw);
+ pw.flush();
+ msg = msg + sw.toString() + "</pre></body></html>";
+ this.writeReply(ctx, 500, "text/html", "Internal
Error", msg);
+ }
+ }
+
+ void handlePut(URI uri, Bucket data, ToadletContext ctx)
+ throws ToadletContextClosedException, IOException {
+ String notSupported = "<html><head><title>Not
supported</title></head><body>"+
+ "Operation not supported</body>";
+ // FIXME should be 405? Need to let toadlets indicate what is
allowed maybe in a callback?
+ this.writeReply(ctx, 200, "text/html", "OK", notSupported);
+ }
+
+}
Added: trunk/freenet/src/freenet/clients/http/SimpleToadletServer.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/SimpleToadletServer.java
2006-01-13 18:09:01 UTC (rev 7852)
+++ trunk/freenet/src/freenet/clients/http/SimpleToadletServer.java
2006-01-14 01:51:07 UTC (rev 7853)
@@ -0,0 +1,113 @@
+package freenet.clients.http;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.URI;
+import java.util.Iterator;
+import java.util.LinkedList;
+
+import freenet.support.FileLoggerHook;
+import freenet.support.Logger;
+
+public class SimpleToadletServer implements ToadletContainer, Runnable {
+
+ public class ToadletElement {
+ public ToadletElement(Toadlet t2, String urlPrefix) {
+ t = t2;
+ prefix = urlPrefix;
+ }
+ Toadlet t;
+ String prefix;
+ }
+
+ final int port;
+ private final ServerSocket sock;
+ private final LinkedList toadlets;
+
+ public SimpleToadletServer(int i) throws IOException {
+ this.port = i;
+ this.sock = new ServerSocket(port, 0,
InetAddress.getByName("127.0.0.1"));
+ toadlets = new LinkedList();
+ Thread t = new Thread(this, "SimpleToadletServer");
+ t.setDaemon(true);
+ t.start();
+ }
+
+ public void register(Toadlet t, String urlPrefix, boolean atFront) {
+ ToadletElement te = new ToadletElement(t, urlPrefix);
+ if(atFront) toadlets.addFirst(te);
+ else toadlets.addLast(te);
+ t.container = this;
+ }
+
+ public Toadlet findToadlet(URI uri) {
+ Iterator i = toadlets.iterator();
+ while(i.hasNext()) {
+ ToadletElement te = (ToadletElement) i.next();
+
+ if(uri.getPath().startsWith(te.prefix))
+ return te.t;
+ }
+ return null;
+ }
+
+ public static void main(String[] args) throws IOException {
+ File logDir = new File("logs-toadlettest");
+ logDir.mkdir();
+ FileLoggerHook logger = new FileLoggerHook(true, new File(logDir,
"test-1111").getAbsolutePath(),
+ "d (c, t, p): m", "MMM dd, yyyy HH:mm:ss:SSS",
Logger.MINOR, false, true,
+ 1024*1024*1024 /* 1GB of old compressed logfiles */);
+ logger.setInterval("5MINUTES");
+ Logger.setupChain();
+ Logger.globalSetThreshold(Logger.MINOR);
+ Logger.globalAddHook(logger);
+ logger.start();
+ SimpleToadletServer server = new SimpleToadletServer(1111);
+ server.register(new TrivialToadlet(null), "", true);
+ System.out.println("Bound to port 1111.");
+ while(true) {
+ try {
+ Thread.sleep(100000);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ }
+ }
+
+ public void run() {
+ while(true) {
+ try {
+ Socket conn = sock.accept();
+ Logger.minor(this, "Accepted connection");
+ SocketHandler sh = new SocketHandler(conn);
+ } catch (IOException e) {
+ Logger.minor(this, "Got IOException accepting
conn: "+e, e);
+ // Ignore
+ continue;
+ }
+ }
+ }
+
+ public class SocketHandler implements Runnable {
+
+ Socket sock;
+
+ public SocketHandler(Socket conn) {
+ this.sock = conn;
+ Thread t = new Thread(this);
+ t.setDaemon(true);
+ t.start();
+ }
+
+ public void run() {
+ Logger.minor(this, "Handling connection");
+ ToadletContextImpl.handle(sock,
SimpleToadletServer.this);
+ Logger.minor(this, "Handled connection");
+ }
+
+ }
+
+}
Added: trunk/freenet/src/freenet/clients/http/Toadlet.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/Toadlet.java 2006-01-13 18:09:01 UTC
(rev 7852)
+++ trunk/freenet/src/freenet/clients/http/Toadlet.java 2006-01-14 01:51:07 UTC
(rev 7853)
@@ -0,0 +1,98 @@
+package freenet.clients.http;
+
+import java.io.IOException;
+import java.net.URI;
+
+import freenet.client.FetchException;
+import freenet.client.FetchResult;
+import freenet.client.HighLevelSimpleClient;
+import freenet.client.InsertBlock;
+import freenet.client.InserterException;
+import freenet.keys.FreenetURI;
+import freenet.support.Bucket;
+
+/**
+ * Replacement for servlets. Just an easy to use HTTP interface, which is
+ * compatible with continuations (eventually). You must extend this class
+ * and provide the abstract methods. Apologies but we can't do it as an
+ * interface and still have continuation compatibility; we can only
+ * suspend in a member function at this level in most implementations.
+ *
+ * When we eventually implement continuations, these will require very
+ * little thread overhead: We can suspend while running a freenet
+ * request, and only grab a thread when we are either doing I/O or doing
+ * computation in the derived class. We can suspend while doing I/O too;
+ * on systems with NIO, we use that, on systems without it, we just run
+ * the fetch on another (or this) thread. With no need to change any
+ * APIs, and no danger of exploding memory use (unlike the traditional
+ * NIO servlets approach).
+ */
+public abstract class Toadlet {
+
+ protected Toadlet(HighLevelSimpleClient client) {
+ this.client = client;
+ }
+
+ private final HighLevelSimpleClient client;
+ ToadletContainer container;
+
+ /**
+ * Handle a GET request.
+ * Must be implemented by the client.
+ * @param uri The URI (relative to this client's document root) to
+ * be fetched.
+ * @throws IOException
+ * @throws ToadletContextClosedException
+ */
+ abstract void handleGet(URI uri, ToadletContext ctx) throws
ToadletContextClosedException, IOException;
+
+ /**
+ * Likewise for a PUT request.
+ */
+ abstract void handlePut(URI uri, Bucket data, ToadletContext ctx)
throws ToadletContextClosedException, IOException;
+
+ /**
+ * Client calls from the above messages to run a freenet request.
+ * This method may block (or suspend).
+ */
+ FetchResult fetch(FreenetURI uri) throws FetchException {
+ // For now, just run it blocking.
+ return client.fetch(uri);
+ }
+
+ FreenetURI insert(InsertBlock insert, boolean getCHKOnly) throws
InserterException {
+ // For now, just run it blocking.
+ return client.insert(insert, getCHKOnly);
+ }
+
+ /**
+ * Client calls to write a reply to the HTTP requestor.
+ */
+ void writeReply(ToadletContext ctx, int code, String mimeType, String
desc, byte[] data, int offset, int length) throws
ToadletContextClosedException, IOException {
+ ctx.sendReplyHeaders(code, desc, null, mimeType, length);
+ ctx.writeData(data, offset, length);
+ }
+
+ /**
+ * Client calls to write a reply to the HTTP requestor.
+ */
+ void writeReply(ToadletContext ctx, int code, String mimeType, String
desc, Bucket data) throws ToadletContextClosedException, IOException {
+ ctx.sendReplyHeaders(code, desc, null, mimeType, data.size());
+ ctx.writeData(data);
+ }
+
+ void writeReply(ToadletContext ctx, int code, String mimeType, String
desc, String reply) throws ToadletContextClosedException, IOException {
+ byte[] buf = reply.getBytes("ISO-8859-1");
+ ctx.sendReplyHeaders(code, desc, null, mimeType, buf.length);
+ ctx.writeData(buf, 0, buf.length);
+ }
+
+ /**
+ * Get the client impl. DO NOT call the blocking methods on it!!
+ * Just use it for configuration etc.
+ */
+ protected HighLevelSimpleClient getClientImpl() {
+ return client;
+ }
+
+}
Added: trunk/freenet/src/freenet/clients/http/ToadletContainer.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/ToadletContainer.java
2006-01-13 18:09:01 UTC (rev 7852)
+++ trunk/freenet/src/freenet/clients/http/ToadletContainer.java
2006-01-14 01:51:07 UTC (rev 7853)
@@ -0,0 +1,19 @@
+package freenet.clients.http;
+
+import java.net.URI;
+
+/** Interface for toadlet containers. Toadlets should register here. */
+public interface ToadletContainer {
+
+ /** Register a Toadlet. All requests whose URL starts with the given
+ * prefix will be passed to this toadlet.
+ * @param atFront If true, add to front of list (where is checked
first),
+ * else add to back of list (where is checked last).
+ */
+ public void register(Toadlet t, String urlPrefix, boolean atFront);
+
+ /**
+ * Find a Toadlet by URI.
+ */
+ public Toadlet findToadlet(URI uri);
+}
Added: trunk/freenet/src/freenet/clients/http/ToadletContext.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/ToadletContext.java 2006-01-13
18:09:01 UTC (rev 7852)
+++ trunk/freenet/src/freenet/clients/http/ToadletContext.java 2006-01-14
01:51:07 UTC (rev 7853)
@@ -0,0 +1,34 @@
+package freenet.clients.http;
+
+import java.io.IOException;
+
+import freenet.support.Bucket;
+import freenet.support.MultiValueTable;
+
+/**
+ * Object represents context for a single request. Is used as a token,
+ * when the Toadlet wants to e.g. write a reply.
+ */
+public interface ToadletContext {
+
+ /**
+ * Write reply headers.
+ * @param code HTTP code.
+ * @param desc HTTP code description.
+ * @param mvt Any extra headers.
+ * @param mimeType The MIME type of the reply.
+ * @param length The length of the reply.
+ */
+ void sendReplyHeaders(int code, String desc, MultiValueTable mvt,
String mimeType, long length) throws ToadletContextClosedException, IOException;
+
+ /**
+ * Write data. Note you must send reply headers first.
+ */
+ void writeData(byte[] data, int offset, int length) throws
ToadletContextClosedException, IOException;
+
+ /**
+ * Write data from a bucket. You must send reply headers first.
+ */
+ void writeData(Bucket data) throws ToadletContextClosedException,
IOException;
+
+}
Added: trunk/freenet/src/freenet/clients/http/ToadletContextClosedException.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/ToadletContextClosedException.java
2006-01-13 18:09:01 UTC (rev 7852)
+++ trunk/freenet/src/freenet/clients/http/ToadletContextClosedException.java
2006-01-14 01:51:07 UTC (rev 7853)
@@ -0,0 +1,5 @@
+package freenet.clients.http;
+
+public class ToadletContextClosedException extends Exception {
+
+}
Added: trunk/freenet/src/freenet/clients/http/ToadletContextImpl.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/ToadletContextImpl.java
2006-01-13 18:09:01 UTC (rev 7852)
+++ trunk/freenet/src/freenet/clients/http/ToadletContextImpl.java
2006-01-14 01:51:07 UTC (rev 7853)
@@ -0,0 +1,240 @@
+package freenet.clients.http;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Enumeration;
+
+import freenet.support.Bucket;
+import freenet.support.BucketTools;
+import freenet.support.Logger;
+import freenet.support.MultiValueTable;
+import freenet.support.io.LineReadingInputStream;
+import freenet.support.io.TooLongException;
+
+/**
+ * ToadletContext implementation, including all the icky HTTP parsing etc.
+ * An actual ToadletContext object represents a request, after we have parsed
the
+ * headers. It provides methods to send replies.
+ * @author root
+ *
+ */
+public class ToadletContextImpl implements ToadletContext {
+
+ private final Socket sock;
+ private final MultiValueTable headers;
+ private final OutputStream sockOutputStream;
+ /** Is the context closed? If so, don't allow any more writes. This is
because there
+ * may be later requests.
+ */
+ private boolean closed;
+
+ public ToadletContextImpl(Socket sock, MultiValueTable headers) throws
IOException {
+ this.sock = sock;
+ this.headers = headers;
+ this.closed = false;
+ sockOutputStream = sock.getOutputStream();
+ }
+
+ private void close() {
+ closed = true;
+ }
+
+ private void sendMethodNotAllowed(String method, boolean
shouldDisconnect) throws ToadletContextClosedException, IOException {
+ if(closed) throw new ToadletContextClosedException();
+ MultiValueTable mvt = new MultiValueTable();
+ mvt.put("Allow", "GET, PUT");
+ sendError(sockOutputStream, 405, "Method not allowed",
shouldDisconnect, mvt);
+ }
+
+ private static void sendError(OutputStream os, int code, String
message, boolean shouldDisconnect, MultiValueTable mvt) throws IOException {
+ sendError(os, code, message,
"<html><head><title>"+message+"</title></head><body><h1>"+message+"</h1></body>",
shouldDisconnect, mvt);
+ }
+
+ private static void sendError(OutputStream os, int code, String
message, String htmlMessage, boolean disconnect, MultiValueTable mvt) throws
IOException {
+ if(mvt == null) mvt = new MultiValueTable();
+ if(disconnect)
+ mvt.put("Connection", "close");
+ byte[] messageBytes = htmlMessage.getBytes("ISO-8859-1");
+ sendReplyHeaders(os, code, message, mvt, "text/html",
messageBytes.length);
+ os.write(messageBytes);
+ }
+
+ private void sendNoToadletError(boolean shouldDisconnect) throws
ToadletContextClosedException, IOException {
+ if(closed) throw new ToadletContextClosedException();
+ sendError(sockOutputStream, 404, "Service not found",
shouldDisconnect, null);
+ }
+
+ private static void sendURIParseError(OutputStream os, boolean
shouldDisconnect) throws IOException {
+ sendError(os, 400, "URI parse error", shouldDisconnect, null);
+ }
+
+ public void sendReplyHeaders(int replyCode, String replyDescription,
MultiValueTable mvt, String mimeType, long contentLength) throws
ToadletContextClosedException, IOException {
+ if(closed) throw new ToadletContextClosedException();
+ sendReplyHeaders(sockOutputStream, replyCode, replyDescription,
mvt, mimeType, contentLength);
+ }
+
+ static void sendReplyHeaders(OutputStream sockOutputStream, int
replyCode, String replyDescription, MultiValueTable mvt, String mimeType, long
contentLength) throws IOException {
+ // Construct headers
+ if(mvt == null)
+ mvt = new MultiValueTable();
+ if(mimeType != null)
+ mvt.put("content-type", mimeType);
+ if(contentLength >= 0)
+ mvt.put("content-length", Long.toString(contentLength));
+ StringBuffer buf = new StringBuffer(1024);
+ buf.append("HTTP/1.1 ");
+ buf.append(replyCode);
+ buf.append(' ');
+ buf.append(replyDescription);
+ buf.append("\r\n");
+ for(Enumeration e = mvt.keys();e.hasMoreElements();) {
+ String key = (String) e.nextElement();
+ Object[] list = mvt.getArray(key);
+ for(int i=0;i<list.length;i++) {
+ String val = (String) list[i];
+ buf.append(key);
+ buf.append(": ");
+ buf.append(val);
+ buf.append("\r\n");
+ }
+ }
+ buf.append("\r\n");
+ sockOutputStream.write(buf.toString().getBytes("US-ASCII"));
+ }
+
+ /**
+ * Handle an incoming connection. Blocking, obviously.
+ */
+ public static void handle(Socket sock, ToadletContainer container) {
+ try {
+ InputStream is = sock.getInputStream();
+
+ LineReadingInputStream lis = new
LineReadingInputStream(is);
+
+ while(true) {
+
+ String firstLine = lis.readLine(32768, 128);
+
+ Logger.minor(ToadletContextImpl.class, "first
line: "+firstLine);
+
+ String[] split = firstLine.split(" ");
+
+ if(split.length != 3)
+ throw new ParseException("Could not
parse request line (split.length="+split.length+"): "+firstLine);
+
+ if(!split[2].startsWith("HTTP/1."))
+ throw new ParseException("Unrecognized
protocol "+split[2]);
+
+ URI uri;
+ try {
+ uri = new URI(split[1]);
+ } catch (URISyntaxException e) {
+
sendURIParseError(sock.getOutputStream(), true);
+ return;
+ }
+
+ String method = split[0];
+
+ MultiValueTable headers = new MultiValueTable();
+
+ while(true) {
+ String line = lis.readLine(32768, 128);
+
System.out.println("Length="+line.length()+": "+line);
+ if(line.length() == 0) break;
+ int index = line.indexOf(':');
+ String before = line.substring(0,
index);
+ String after = line.substring(index+1);
+ after = after.trim();
+ headers.put(before, after);
+ }
+
+ // Handle it.
+
+ Toadlet t = container.findToadlet(uri);
+
+ ToadletContextImpl ctx = new
ToadletContextImpl(sock, headers);
+
+ boolean shouldDisconnect =
shouldDisconnectAfterHandled(split[2].equals("HTTP/1.0"), headers);
+
+ if(t == null)
+
ctx.sendNoToadletError(shouldDisconnect);
+
+ if(method.equals("GET")) {
+
+ t.handleGet(uri, ctx);
+ ctx.close();
+
+ } else if(method.equals("POST")) {
+
+ Logger.error(ToadletContextImpl.class,
"POST not supported");
+ ctx.sendMethodNotAllowed(method,
shouldDisconnect);
+ ctx.close();
+
+ } else {
+ ctx.sendMethodNotAllowed(method,
shouldDisconnect);
+ ctx.close();
+ }
+
+ if(shouldDisconnect) {
+ sock.close();
+ return;
+ }
+ }
+
+ } catch (ParseException e) {
+ try {
+ sendError(sock.getOutputStream(), 400, "Parse
error: "+e.getMessage(), true, null);
+ } catch (IOException e1) {
+ // Ignore
+ }
+ } catch (TooLongException e) {
+ try {
+ sendError(sock.getOutputStream(), 400, "Line
too long parsing headers", true, null);
+ } catch (IOException e1) {
+ // Ignore
+ }
+ } catch (IOException e) {
+ return;
+ } catch (ToadletContextClosedException e) {
+ Logger.error(ToadletContextImpl.class,
"ToadletContextClosedException while handling connection!");
+ return;
+ }
+ }
+
+ private static boolean shouldDisconnectAfterHandled(boolean isHTTP10,
MultiValueTable headers) {
+ String connection = (String) headers.get("connection");
+ if(connection != null) {
+ if(connection.equalsIgnoreCase("close"))
+ return true;
+
+ if(connection.equalsIgnoreCase("keep-alive"))
+ return false;
+ }
+ if(!isHTTP10) return true;
+ // WTF?
+ return true;
+ }
+
+ static class ParseException extends Exception {
+
+ ParseException(String string) {
+ super(string);
+ }
+
+ }
+
+ public void writeData(byte[] data, int offset, int length) throws
ToadletContextClosedException, IOException {
+ if(closed) throw new ToadletContextClosedException();
+ sockOutputStream.write(data, offset, length);
+ }
+
+ public void writeData(Bucket data) throws
ToadletContextClosedException, IOException {
+ if(closed) throw new ToadletContextClosedException();
+ BucketTools.copyTo(data, sockOutputStream, Long.MAX_VALUE);
+ }
+
+}
Added: trunk/freenet/src/freenet/clients/http/TrivialToadlet.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/TrivialToadlet.java 2006-01-13
18:09:01 UTC (rev 7852)
+++ trunk/freenet/src/freenet/clients/http/TrivialToadlet.java 2006-01-14
01:51:07 UTC (rev 7853)
@@ -0,0 +1,32 @@
+package freenet.clients.http;
+
+import java.io.IOException;
+import java.net.URI;
+
+import freenet.client.HighLevelSimpleClient;
+import freenet.support.Bucket;
+import freenet.support.HTMLEncoder;
+
+public class TrivialToadlet extends Toadlet {
+
+ TrivialToadlet(HighLevelSimpleClient client) {
+ super(client);
+ }
+
+ void handleGet(URI uri, ToadletContext ctx) throws
ToadletContextClosedException, IOException {
+ String fetched = uri.toString();
+ String encFetched = HTMLEncoder.encode(fetched);
+ String reply = "<html><head><title>You requested "+encFetched+
+ "</title></head><body>You fetched <a
href=\""+encFetched+"\">"+
+ encFetched+"</a>.</body></html>";
+ this.writeReply(ctx, 200, "text/html", "OK", reply);
+ }
+
+ void handlePut(URI uri, Bucket data, ToadletContext ctx) throws
ToadletContextClosedException, IOException {
+ String notSupported = "<html><head><title>Not
supported</title></head><body>"+
+ "Operation not supported</body>";
+ // This really should be 405, but then we'd have to put an
Allow header in.
+ this.writeReply(ctx, 200, "text/html", "OK", notSupported);
+ }
+
+}
Modified: trunk/freenet/src/freenet/node/Node.java
===================================================================
--- trunk/freenet/src/freenet/node/Node.java 2006-01-13 18:09:01 UTC (rev
7852)
+++ trunk/freenet/src/freenet/node/Node.java 2006-01-14 01:51:07 UTC (rev
7853)
@@ -26,6 +26,8 @@
import freenet.client.ArchiveManager;
import freenet.client.HighLevelSimpleClient;
import freenet.client.HighLevelSimpleClientImpl;
+import freenet.clients.http.FproxyToadlet;
+import freenet.clients.http.SimpleToadletServer;
import freenet.crypt.DSAPublicKey;
import freenet.crypt.DiffieHellman;
import freenet.crypt.RandomSource;
@@ -328,6 +330,12 @@
Thread t = new Thread(new MemoryChecker(), "Memory checker");
t.setPriority(Thread.MAX_PRIORITY);
t.start();
+ SimpleToadletServer server = new SimpleToadletServer(port+2001);
+ FproxyToadlet fproxy = new
FproxyToadlet(n.makeClient(RequestStarter.INTERACTIVE_PRIORITY_CLASS,
(short)0));
+ server.register(fproxy, "/", false);
+ System.out.println("Starting fproxy on port "+(port+2001));
+ //server.register(fproxy, "/SSK@", false);
+ //server.register(fproxy, "/KSK@", false);
}
// FIXME - the whole overrideIP thing is a hack to avoid config
Modified: trunk/freenet/src/freenet/node/Version.java
===================================================================
--- trunk/freenet/src/freenet/node/Version.java 2006-01-13 18:09:01 UTC (rev
7852)
+++ trunk/freenet/src/freenet/node/Version.java 2006-01-14 01:51:07 UTC (rev
7853)
@@ -20,7 +20,7 @@
public static final String protocolVersion = "1.0";
/** The build number of the current revision */
- public static final int buildNumber = 348;
+ public static final int buildNumber = 349;
/** Oldest build of Fred we will talk to */
public static final int lastGoodBuild = 348;
Added: trunk/freenet/src/freenet/support/HTMLDecoder.java
===================================================================
--- trunk/freenet/src/freenet/support/HTMLDecoder.java 2006-01-13 18:09:01 UTC
(rev 7852)
+++ trunk/freenet/src/freenet/support/HTMLDecoder.java 2006-01-14 01:51:07 UTC
(rev 7853)
@@ -0,0 +1,411 @@
+package freenet.support;
+
+import java.util.HashMap;
+
+/**
+ * Description: Utility for converting character references e.g.: < >
+ * " å И 水
+ *
+ * @author Yves Lempereur (avian)
+ */
+public class HTMLDecoder {
+
+ private static final HashMap charTable;
+
+ public static String decode(String s) {
+ String t;
+ Character ch;
+ int tmpPos, i;
+
+ int maxPos = s.length();
+ StringBuffer sb = new StringBuffer(maxPos);
+ int curPos = 0;
+ while (curPos < maxPos) {
+ char c = s.charAt(curPos++);
+ if (c == '&') {
+ tmpPos = curPos;
+ if (tmpPos < maxPos) {
+ char d = s.charAt(tmpPos++);
+ if (d == '#') {
+ if (tmpPos < maxPos) {
+ d = s.charAt(tmpPos++);
+ if (d == 'x' || d ==
'X') {
+ if (tmpPos <
maxPos) {
+ d =
s.charAt(tmpPos++);
+ if
(isHexDigit(d)) {
+
while (tmpPos < maxPos) {
+
d = s.charAt(tmpPos++);
+
if (!isHexDigit(d)) {
+
if (d == ';') {
+
t =
+
s.substring(
+
curPos + 2,
+
tmpPos - 1);
+
try {
+
i =
+
Integer.parseInt(
+
t,
+
16);
+
if (i >= 0
+
&& i < 65536) {
+
c = (char) i;
+
curPos = tmpPos;
+
}
+
} catch (NumberFormatException e) {
+
}
+
}
+
break;
+
}
+
}
+ }
+ }
+ } else if (isDigit(d)) {
+ while (tmpPos <
maxPos) {
+ d =
s.charAt(tmpPos++);
+ if
(!isDigit(d)) {
+
if (d == ';') {
+
t =
+
s.substring(
+
curPos + 1,
+
tmpPos - 1);
+
try {
+
i = Integer.parseInt(t);
+
if (i >= 0 && i < 65536) {
+
c = (char) i;
+
curPos = tmpPos;
+
}
+
} catch (NumberFormatException e) {
+
}
+
}
+
break;
+ }
+ }
+ }
+ }
+ } else if (isLetter(d)) {
+ while (tmpPos < maxPos) {
+ d = s.charAt(tmpPos++);
+ if
(!isLetterOrDigit(d)) {
+ if (d == ';') {
+ t =
s.substring(curPos, tmpPos - 1);
+ ch =
(Character) charTable.get(t);
+ if (ch
!= null) {
+
c = ch.charValue();
+
curPos = tmpPos;
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ sb.append(c);
+ }
+ return sb.toString();
+ }
+
+ private static boolean isLetterOrDigit(char c) {
+ return isLetter(c) || isDigit(c);
+ }
+
+ private static boolean isHexDigit(char c) {
+ return isHexLetter(c) || isDigit(c);
+ }
+
+ private static boolean isLetter(char c) {
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
+ }
+
+ private static boolean isHexLetter(char c) {
+ return (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
+ }
+
+ private static boolean isDigit(char c) {
+ return c >= '0' && c <= '9';
+ }
+
+ public static String compact(String s) {
+ int maxPos = s.length();
+ StringBuffer sb = new StringBuffer(maxPos);
+ int curPos = 0;
+ while (curPos < maxPos) {
+ char c = s.charAt(curPos++);
+ if (isWhitespace(c)) {
+ while (curPos < maxPos &&
isWhitespace(s.charAt(curPos))) {
+ curPos++;
+ }
+ c = '\u0020';
+ }
+ sb.append(c);
+ }
+ return sb.toString();
+ }
+
+ // HTML is very particular about what constitutes white space.
+ public static boolean isWhitespace(char ch) {
+ return ch == '\u0020'
+ || ch == '\r'
+ || ch == '\n'
+ || ch == '\u0009'
+ || ch == '\u000c'
+ || ch == '\u200b';
+ }
+
+ static {
+ charTable = new HashMap();
+ charTable.put("quot", new Character((char) 34));
+ charTable.put("amp", new Character((char) 38));
+ charTable.put("apos", new Character((char) 39));
+ charTable.put("lt", new Character((char) 60));
+ charTable.put("gt", new Character((char) 62));
+ charTable.put("nbsp", new Character((char) 160));
+ charTable.put("iexcl", new Character((char) 161));
+ charTable.put("cent", new Character((char) 162));
+ charTable.put("pound", new Character((char) 163));
+ charTable.put("curren", new Character((char) 164));
+ charTable.put("yen", new Character((char) 165));
+ charTable.put("brvbar", new Character((char) 166));
+ charTable.put("sect", new Character((char) 167));
+ charTable.put("uml", new Character((char) 168));
+ charTable.put("copy", new Character((char) 169));
+ charTable.put("ordf", new Character((char) 170));
+ charTable.put("laquo", new Character((char) 171));
+ charTable.put("not", new Character((char) 172));
+ charTable.put("shy", new Character((char) 173));
+ charTable.put("reg", new Character((char) 174));
+ charTable.put("macr", new Character((char) 175));
+ charTable.put("deg", new Character((char) 176));
+ charTable.put("plusmn", new Character((char) 177));
+ charTable.put("sup2", new Character((char) 178));
+ charTable.put("sup3", new Character((char) 179));
+ charTable.put("acute", new Character((char) 180));
+ charTable.put("micro", new Character((char) 181));
+ charTable.put("para", new Character((char) 182));
+ charTable.put("middot", new Character((char) 183));
+ charTable.put("cedil", new Character((char) 184));
+ charTable.put("sup1", new Character((char) 185));
+ charTable.put("ordm", new Character((char) 186));
+ charTable.put("raquo", new Character((char) 187));
+ charTable.put("frac14", new Character((char) 188));
+ charTable.put("frac12", new Character((char) 189));
+ charTable.put("frac34", new Character((char) 190));
+ charTable.put("iquest", new Character((char) 191));
+ charTable.put("Agrave", new Character((char) 192));
+ charTable.put("Aacute", new Character((char) 193));
+ charTable.put("Acirc", new Character((char) 194));
+ charTable.put("Atilde", new Character((char) 195));
+ charTable.put("Auml", new Character((char) 196));
+ charTable.put("Aring", new Character((char) 197));
+ charTable.put("AElig", new Character((char) 198));
+ charTable.put("Ccedil", new Character((char) 199));
+ charTable.put("Egrave", new Character((char) 200));
+ charTable.put("Eacute", new Character((char) 201));
+ charTable.put("Ecirc", new Character((char) 202));
+ charTable.put("Euml", new Character((char) 203));
+ charTable.put("Igrave", new Character((char) 204));
+ charTable.put("Iacute", new Character((char) 205));
+ charTable.put("Icirc", new Character((char) 206));
+ charTable.put("Iuml", new Character((char) 207));
+ charTable.put("ETH", new Character((char) 208));
+ charTable.put("Ntilde", new Character((char) 209));
+ charTable.put("Ograve", new Character((char) 210));
+ charTable.put("Oacute", new Character((char) 211));
+ charTable.put("Ocirc", new Character((char) 212));
+ charTable.put("Otilde", new Character((char) 213));
+ charTable.put("Ouml", new Character((char) 214));
+ charTable.put("times", new Character((char) 215));
+ charTable.put("Oslash", new Character((char) 216));
+ charTable.put("Ugrave", new Character((char) 217));
+ charTable.put("Uacute", new Character((char) 218));
+ charTable.put("Ucirc", new Character((char) 219));
+ charTable.put("Uuml", new Character((char) 220));
+ charTable.put("Yacute", new Character((char) 221));
+ charTable.put("THORN", new Character((char) 222));
+ charTable.put("szlig", new Character((char) 223));
+ charTable.put("agrave", new Character((char) 224));
+ charTable.put("aacute", new Character((char) 225));
+ charTable.put("acirc", new Character((char) 226));
+ charTable.put("atilde", new Character((char) 227));
+ charTable.put("auml", new Character((char) 228));
+ charTable.put("aring", new Character((char) 229));
+ charTable.put("aelig", new Character((char) 230));
+ charTable.put("ccedil", new Character((char) 231));
+ charTable.put("egrave", new Character((char) 232));
+ charTable.put("eacute", new Character((char) 233));
+ charTable.put("ecirc", new Character((char) 234));
+ charTable.put("euml", new Character((char) 235));
+ charTable.put("igrave", new Character((char) 236));
+ charTable.put("iacute", new Character((char) 237));
+ charTable.put("icirc", new Character((char) 238));
+ charTable.put("iuml", new Character((char) 239));
+ charTable.put("eth", new Character((char) 240));
+ charTable.put("ntilde", new Character((char) 241));
+ charTable.put("ograve", new Character((char) 242));
+ charTable.put("oacute", new Character((char) 243));
+ charTable.put("ocirc", new Character((char) 244));
+ charTable.put("otilde", new Character((char) 245));
+ charTable.put("ouml", new Character((char) 246));
+ charTable.put("divide", new Character((char) 247));
+ charTable.put("oslash", new Character((char) 248));
+ charTable.put("ugrave", new Character((char) 249));
+ charTable.put("uacute", new Character((char) 250));
+ charTable.put("ucirc", new Character((char) 251));
+ charTable.put("uuml", new Character((char) 252));
+ charTable.put("yacute", new Character((char) 253));
+ charTable.put("thorn", new Character((char) 254));
+ charTable.put("yuml", new Character((char) 255));
+ charTable.put("OElig", new Character((char) 338));
+ charTable.put("oelig", new Character((char) 339));
+ charTable.put("Scaron", new Character((char) 352));
+ charTable.put("scaron", new Character((char) 353));
+ charTable.put("Yuml", new Character((char) 376));
+ charTable.put("fnof", new Character((char) 402));
+ charTable.put("circ", new Character((char) 710));
+ charTable.put("tilde", new Character((char) 732));
+ charTable.put("Alpha", new Character((char) 913));
+ charTable.put("Beta", new Character((char) 914));
+ charTable.put("Gamma", new Character((char) 915));
+ charTable.put("Delta", new Character((char) 916));
+ charTable.put("Epsilon", new Character((char) 917));
+ charTable.put("Zeta", new Character((char) 918));
+ charTable.put("Eta", new Character((char) 919));
+ charTable.put("Theta", new Character((char) 920));
+ charTable.put("Iota", new Character((char) 921));
+ charTable.put("Kappa", new Character((char) 922));
+ charTable.put("Lambda", new Character((char) 923));
+ charTable.put("Mu", new Character((char) 924));
+ charTable.put("Nu", new Character((char) 925));
+ charTable.put("Xi", new Character((char) 926));
+ charTable.put("Omicron", new Character((char) 927));
+ charTable.put("Pi", new Character((char) 928));
+ charTable.put("Rho", new Character((char) 929));
+ charTable.put("Sigma", new Character((char) 931));
+ charTable.put("Tau", new Character((char) 932));
+ charTable.put("Upsilon", new Character((char) 933));
+ charTable.put("Phi", new Character((char) 934));
+ charTable.put("Chi", new Character((char) 935));
+ charTable.put("Psi", new Character((char) 936));
+ charTable.put("Omega", new Character((char) 937));
+ charTable.put("alpha", new Character((char) 945));
+ charTable.put("beta", new Character((char) 946));
+ charTable.put("gamma", new Character((char) 947));
+ charTable.put("delta", new Character((char) 948));
+ charTable.put("epsilon", new Character((char) 949));
+ charTable.put("zeta", new Character((char) 950));
+ charTable.put("eta", new Character((char) 951));
+ charTable.put("theta", new Character((char) 952));
+ charTable.put("iota", new Character((char) 953));
+ charTable.put("kappa", new Character((char) 954));
+ charTable.put("lambda", new Character((char) 955));
+ charTable.put("mu", new Character((char) 956));
+ charTable.put("nu", new Character((char) 957));
+ charTable.put("xi", new Character((char) 958));
+ charTable.put("omicron", new Character((char) 959));
+ charTable.put("pi", new Character((char) 960));
+ charTable.put("rho", new Character((char) 961));
+ charTable.put("sigmaf", new Character((char) 962));
+ charTable.put("sigma", new Character((char) 963));
+ charTable.put("tau", new Character((char) 964));
+ charTable.put("upsilon", new Character((char) 965));
+ charTable.put("phi", new Character((char) 966));
+ charTable.put("chi", new Character((char) 967));
+ charTable.put("psi", new Character((char) 968));
+ charTable.put("omega", new Character((char) 969));
+ charTable.put("thetasym", new Character((char) 977));
+ charTable.put("upsih", new Character((char) 978));
+ charTable.put("piv", new Character((char) 982));
+ charTable.put("ensp", new Character((char) 8194));
+ charTable.put("emsp", new Character((char) 8195));
+ charTable.put("thinsp", new Character((char) 8201));
+ charTable.put("zwnj", new Character((char) 8204));
+ charTable.put("zwj", new Character((char) 8205));
+ charTable.put("lrm", new Character((char) 8206));
+ charTable.put("rlm", new Character((char) 8207));
+ charTable.put("ndash", new Character((char) 8211));
+ charTable.put("mdash", new Character((char) 8212));
+ charTable.put("lsquo", new Character((char) 8216));
+ charTable.put("rsquo", new Character((char) 8217));
+ charTable.put("sbquo", new Character((char) 8218));
+ charTable.put("ldquo", new Character((char) 8220));
+ charTable.put("rdquo", new Character((char) 8221));
+ charTable.put("bdquo", new Character((char) 8222));
+ charTable.put("dagger", new Character((char) 8224));
+ charTable.put("Dagger", new Character((char) 8225));
+ charTable.put("bull", new Character((char) 8226));
+ charTable.put("hellip", new Character((char) 8230));
+ charTable.put("permil", new Character((char) 8240));
+ charTable.put("prime", new Character((char) 8242));
+ charTable.put("Prime", new Character((char) 8243));
+ charTable.put("lsaquo", new Character((char) 8249));
+ charTable.put("rsaquo", new Character((char) 8250));
+ charTable.put("oline", new Character((char) 8254));
+ charTable.put("frasl", new Character((char) 8260));
+ charTable.put("euro", new Character((char) 8364));
+ charTable.put("image", new Character((char) 8465));
+ charTable.put("weierp", new Character((char) 8472));
+ charTable.put("real", new Character((char) 8476));
+ charTable.put("trade", new Character((char) 8482));
+ charTable.put("alefsym", new Character((char) 8501));
+ charTable.put("larr", new Character((char) 8592));
+ charTable.put("uarr", new Character((char) 8593));
+ charTable.put("rarr", new Character((char) 8594));
+ charTable.put("darr", new Character((char) 8595));
+ charTable.put("harr", new Character((char) 8596));
+ charTable.put("crarr", new Character((char) 8629));
+ charTable.put("lArr", new Character((char) 8656));
+ charTable.put("uArr", new Character((char) 8657));
+ charTable.put("rArr", new Character((char) 8658));
+ charTable.put("dArr", new Character((char) 8659));
+ charTable.put("hArr", new Character((char) 8660));
+ charTable.put("forall", new Character((char) 8704));
+ charTable.put("part", new Character((char) 8706));
+ charTable.put("exist", new Character((char) 8707));
+ charTable.put("empty", new Character((char) 8709));
+ charTable.put("nabla", new Character((char) 8711));
+ charTable.put("isin", new Character((char) 8712));
+ charTable.put("notin", new Character((char) 8713));
+ charTable.put("ni", new Character((char) 8715));
+ charTable.put("prod", new Character((char) 8719));
+ charTable.put("sum", new Character((char) 8721));
+ charTable.put("minus", new Character((char) 8722));
+ charTable.put("lowast", new Character((char) 8727));
+ charTable.put("radic", new Character((char) 8730));
+ charTable.put("prop", new Character((char) 8733));
+ charTable.put("infin", new Character((char) 8734));
+ charTable.put("ang", new Character((char) 8736));
+ charTable.put("and", new Character((char) 8743));
+ charTable.put("or", new Character((char) 8744));
+ charTable.put("cap", new Character((char) 8745));
+ charTable.put("cup", new Character((char) 8746));
+ charTable.put("int", new Character((char) 8747));
+ charTable.put("there4", new Character((char) 8756));
+ charTable.put("sim", new Character((char) 8764));
+ charTable.put("cong", new Character((char) 8773));
+ charTable.put("asymp", new Character((char) 8776));
+ charTable.put("ne", new Character((char) 8800));
+ charTable.put("equiv", new Character((char) 8801));
+ charTable.put("le", new Character((char) 8804));
+ charTable.put("ge", new Character((char) 8805));
+ charTable.put("sub", new Character((char) 8834));
+ charTable.put("sup", new Character((char) 8835));
+ charTable.put("nsub", new Character((char) 8836));
+ charTable.put("sube", new Character((char) 8838));
+ charTable.put("supe", new Character((char) 8839));
+ charTable.put("oplus", new Character((char) 8853));
+ charTable.put("otimes", new Character((char) 8855));
+ charTable.put("perp", new Character((char) 8869));
+ charTable.put("sdot", new Character((char) 8901));
+ charTable.put("lceil", new Character((char) 8968));
+ charTable.put("rceil", new Character((char) 8969));
+ charTable.put("lfloor", new Character((char) 8970));
+ charTable.put("rfloor", new Character((char) 8971));
+ charTable.put("lang", new Character((char) 9001));
+ charTable.put("rang", new Character((char) 9002));
+ charTable.put("loz", new Character((char) 9674));
+ charTable.put("spades", new Character((char) 9824));
+ charTable.put("clubs", new Character((char) 9827));
+ charTable.put("hearts", new Character((char) 9829));
+ charTable.put("diams", new Character((char) 9830));
+ }
+}
Added: trunk/freenet/src/freenet/support/HTMLEncoder.java
===================================================================
--- trunk/freenet/src/freenet/support/HTMLEncoder.java 2006-01-13 18:09:01 UTC
(rev 7852)
+++ trunk/freenet/src/freenet/support/HTMLEncoder.java 2006-01-14 01:51:07 UTC
(rev 7853)
@@ -0,0 +1,39 @@
+package freenet.support;
+
+/**
+ * Originally from com.websiteasp.ox pasckage.
+ *
+ * Author: Yves Lempereur
+ */
+public class HTMLEncoder {
+
+ public static String encode(String s) {
+ int n = s.length();
+ StringBuffer sb = new StringBuffer(n);
+ for (int i = 0; i < n; i++) {
+ char c = s.charAt(i);
+ switch (c) {
+ case '"' :
+ sb.append(""");
+ break;
+ case '&' :
+ sb.append("&");
+ break;
+ // case
'\'':
+ //
sb.append("'");
+ //
break;
+ case '<' :
+ sb.append("<");
+ break;
+ case '>' :
+ sb.append(">");
+ break;
+ default :
+ sb.append(c);
+ break;
+ }
+ }
+ return sb.toString();
+ }
+
+}
Added: trunk/freenet/src/freenet/support/LimitedEnumeration.java
===================================================================
--- trunk/freenet/src/freenet/support/LimitedEnumeration.java 2006-01-13
18:09:01 UTC (rev 7852)
+++ trunk/freenet/src/freenet/support/LimitedEnumeration.java 2006-01-14
01:51:07 UTC (rev 7853)
@@ -0,0 +1,34 @@
+package freenet.support;
+
+import java.util.Enumeration;
+import java.util.NoSuchElementException;
+
+/** We kept remaking this everywhere so wtf.
+ * @author tavin
+ */
+public final class LimitedEnumeration implements Enumeration {
+
+ private Object next;
+
+ public LimitedEnumeration() {
+ next = null;
+ }
+
+ public LimitedEnumeration(Object loner) {
+ next = loner;
+ }
+
+ public final boolean hasMoreElements() {
+ return next != null;
+ }
+
+ public final Object nextElement() {
+ if (next == null) throw new NoSuchElementException();
+ try {
+ return next;
+ }
+ finally {
+ next = null;
+ }
+ }
+}
Added: trunk/freenet/src/freenet/support/MultiValueTable.java
===================================================================
--- trunk/freenet/src/freenet/support/MultiValueTable.java 2006-01-13
18:09:01 UTC (rev 7852)
+++ trunk/freenet/src/freenet/support/MultiValueTable.java 2006-01-14
01:51:07 UTC (rev 7853)
@@ -0,0 +1,158 @@
+package freenet.support;
+import java.util.*;
+/**
+ * A hashtable that can store several values for each entry.
+ *
+ * @author oskar
+ */
+
+public class MultiValueTable {
+
+ private Hashtable table;
+ private int ies;
+
+ public MultiValueTable() {
+ this(16, 3);
+ }
+
+ public MultiValueTable(int initialSize) {
+ this(initialSize, 3);
+ }
+
+ public MultiValueTable(int initialSize, int initialEntrySize) {
+ table = new Hashtable(initialSize);
+ ies = initialEntrySize;
+ }
+
+ public void put(Object key, Object value) {
+ synchronized (table) {
+ Vector v = (Vector) table.get(key);
+ if (v == null) {
+ v = new Vector(ies);
+ table.put(key, v);
+ }
+ v.addElement(value);
+ }
+ }
+
+ /**
+ * Returns the first element for this key.
+ */
+ public Object get(Object key) {
+ synchronized (table) {
+ Vector v = (Vector) table.get(key);
+ return (v == null ?
+ null :
+ v.firstElement());
+ }
+ }
+
+ public boolean containsKey(Object key) {
+ return table.containsKey(key);
+ }
+
+ public boolean containsElement(Object key, Object value) {
+ synchronized (table) {
+ Vector v = (Vector) table.get(key);
+ return v != null && v.contains(value);
+ }
+ }
+
+ /**
+ * Users will have to handle synchronizing.
+ */
+ public Enumeration getAll(Object key) {
+ Vector v = (Vector) table.get(key);
+ return (v == null ?
+ new LimitedEnumeration(null) :
+ v.elements());
+ }
+
+ public int countAll(Object key) {
+ Vector v = (Vector)table.get(key);
+ if(v != null)
+ return v.size();
+ else
+ return 0;
+ }
+
+ public Object getSync(Object key) {
+ return table.get(key);
+ }
+
+ public Object[] getArray(Object key) {
+ synchronized (table) {
+ Vector v = (Vector) table.get(key);
+ if (v == null)
+ return null;
+ else {
+ Object[] r = new Object[v.size()];
+ v.copyInto(r);
+ return r;
+ }
+ }
+ }
+
+ public void remove(Object key) {
+ table.remove(key);
+ }
+
+ public boolean isEmpty() {
+ return table.isEmpty();
+ }
+
+ public void clear() {
+ table.clear();
+ }
+
+ public boolean removeElement(Object key, Object value) {
+ synchronized (table) {
+ Vector v = (Vector) table.get(key);
+ if (v == null)
+ return false;
+ else {
+ boolean b = v.removeElement(value);
+ if (v.isEmpty())
+ table.remove(key);
+ return b;
+ }
+ }
+ }
+
+ public Enumeration keys() {
+ return table.keys();
+ }
+
+ public Enumeration elements() {
+ if (table.isEmpty())
+ return new LimitedEnumeration(null);
+ else
+ return new MultiValueEnumeration();
+ }
+
+ private class MultiValueEnumeration implements Enumeration {
+ private Enumeration current;
+ private Enumeration global;
+ public MultiValueEnumeration() {
+ global = table.elements();
+ current = ((Vector) global.nextElement()).elements();
+ step();
+ }
+
+ public final void step() {
+ while (!current.hasMoreElements() && global.hasMoreElements())
+ current = ((Vector) global.nextElement()).elements();
+ }
+
+ public final boolean hasMoreElements() {
+ return global.hasMoreElements(); // || current.hasMoreElements();
+ }
+
+ public final Object nextElement() {
+ Object o = current.nextElement();
+ step();
+ return o;
+ }
+ }
+
+}
Added: trunk/freenet/src/freenet/support/URLDecoder.java
===================================================================
--- trunk/freenet/src/freenet/support/URLDecoder.java 2006-01-13 18:09:01 UTC
(rev 7852)
+++ trunk/freenet/src/freenet/support/URLDecoder.java 2006-01-14 01:51:07 UTC
(rev 7853)
@@ -0,0 +1,97 @@
+package freenet.support;
+
+/*
+ This code is part of the Java Adaptive Network Client by Ian Clarke.
+ It is distributed under the GNU Public Licence (GPL) version 2. See
+ http://www.gnu.org/ for further details of the GPL.
+*/
+
+
+/**
+ * The class contains a utility method for converting a
+ * <code>String</code> out of a MIME format called
+ * "<code>x-www-form-urlencoded</code>" format.
+ * <p>
+ * To convert a <code>String</code>, each character is examined in turn:
+ * <ul>
+ * <li>The ASCII characters '<code>a</code>' through '<code>z</code>',
+ * '<code>A</code>' through '<code>Z</code>', and '<code>0</code>'
+ * through '<code>9</code>' remain the same.
+ * <li>The plus sign '<code>+</code>' is converted into a
+ * space character '<code> </code>'.
+ * <li>The percent sign '<code>%</code>' must be followed by a
+ * two-digit hexadecimal number, and is converted into the
+ * corresponding 8-bit character.
+ * <li>The following "safe" characters [RFC 1738] are passed as is,
+ * if they appear:
+ * <code>$ - _ . + ! * ' ( ) ,</code>
+ * <li>Anything else encountered, though strictly speaking illegal,
+ * is passed as is.
+ * </ul>
+ *
+ * @author <a href="http://www.doc.ic.ac.uk/~twh1/">Theodore Hong</a>
+ **/
+
+public class URLDecoder
+{
+ // test harness
+ public static void main(String[] args) throws URLEncodedFormatException {
+ for (int i = 0; i < args.length; i++) {
+ System.out.println(args[i] + " -> " + decode(args[i]));
+ }
+ }
+
+ /**
+ * Characters which will be passed unaltered.
+ **/
+ private static final String safeCharList = "$-_.+!*'(),";
+
+ /**
+ * Translates a string out of x-www-form-urlencoded format.
+ *
+ * @param s String to be translated.
+ * @return the translated String.
+ *
+ **/
+ public static String decode(String s) throws URLEncodedFormatException {
+ if (s.length()==0) return "";
+ int len = s.length();
+ StringBuffer buf = new StringBuffer();
+
+ for (int i = 0; i < len; i++) {
+ char c = s.charAt(i);
+ if (Character.isLetterOrDigit(c))
+ buf.append(c);
+ else if (c == '+')
+ buf.append(' ');
+ else if (safeCharList.indexOf(c) != -1)
+ buf.append(c);
+ else if (c == '%') {
+ if (i >= len - 2) {
+ throw new URLEncodedFormatException(s);
+ } else {
+ char[] hexChars = new char[2];
+
+ hexChars[0] = s.charAt(++i);
+ hexChars[1] = s.charAt(++i);
+
+ String hexval = new String(hexChars);
+ try {
+ long read = Fields.hexToLong(hexval);
+ if (read == 0)
+ throw new URLEncodedFormatException("Can't encode"
+ + " 00");
+ buf.append(new Character((char) read));
+ }
+ catch (NumberFormatException nfe) {
+ throw new URLEncodedFormatException(s);
+ }
+ }
+ }
+ else
+ buf.append(c);
+ // throw new URLEncodedFormatException(s);
+ }
+ return buf.toString();
+ }
+}
Added: trunk/freenet/src/freenet/support/URLEncodedFormatException.java
===================================================================
--- trunk/freenet/src/freenet/support/URLEncodedFormatException.java
2006-01-13 18:09:01 UTC (rev 7852)
+++ trunk/freenet/src/freenet/support/URLEncodedFormatException.java
2006-01-14 01:51:07 UTC (rev 7853)
@@ -0,0 +1,18 @@
+package freenet.support;
+
+/*
+ This code is part of the Java Adaptive Network Client by Ian Clarke.
+ It is distributed under the GNU Public Licence (GPL) version 2. See
+ http://www.gnu.org/ for further details of the GPL.
+*/
+
+
+/**
+ * Thrown when trying to decode a string which is not in
+ * "<code>x-www-form-urlencoded</code>" format.
+ **/
+
+public class URLEncodedFormatException extends Exception {
+ URLEncodedFormatException () {}
+ URLEncodedFormatException (String s) { super(s); }
+}
Added: trunk/freenet/src/freenet/support/URLEncoder.java
===================================================================
--- trunk/freenet/src/freenet/support/URLEncoder.java 2006-01-13 18:09:01 UTC
(rev 7852)
+++ trunk/freenet/src/freenet/support/URLEncoder.java 2006-01-14 01:51:07 UTC
(rev 7853)
@@ -0,0 +1,39 @@
+package freenet.support;
+
+public class URLEncoder {
+ // Moved here from fproxy by amphibian
+ final static String safeURLCharacters =
"@*-./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz";
+
+ /**
+ * Encode a string for inclusion in HTML tags
+ *
+ * @param URL String to encode
+ * @return HTML-safe version of string
+ */
+ public final static String encode(String URL) {
+ StringBuffer enc = new StringBuffer(URL.length());
+ for (int i = 0; i < URL.length(); ++i) {
+ char c = URL.charAt(i);
+ if (safeURLCharacters.indexOf(c) >= 0) {
+ enc.append(c);
+ } else {
+ // Too harsh.
+ // if (c < 0 || c > 255)
+ // throw new RuntimeException("illegal code "+c+" of char
'"+URL.charAt(i)+"'");
+ // else
+
+ // Just keep lsb like:
+ // http://java.sun.com/j2se/1.3/docs/api/java/net/URLEncoder.html
+ c = (char) (c & '\u00ff');
+ if (c < 16) {
+ enc.append("%0");
+ } else {
+ enc.append("%");
+ }
+ enc.append(Integer.toHexString(c));
+ }
+ }
+ return enc.toString();
+ }
+
+}
Added: trunk/freenet/src/freenet/support/io/LineReadingInputStream.java
===================================================================
--- trunk/freenet/src/freenet/support/io/LineReadingInputStream.java
2006-01-13 18:09:01 UTC (rev 7852)
+++ trunk/freenet/src/freenet/support/io/LineReadingInputStream.java
2006-01-14 01:51:07 UTC (rev 7853)
@@ -0,0 +1,39 @@
+package freenet.support.io;
+
+import java.io.EOFException;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A FilterInputStream which provides readLine().
+ */
+public class LineReadingInputStream extends FilterInputStream {
+
+ public LineReadingInputStream(InputStream in) {
+ super(in);
+ }
+
+ /**
+ * Read a line of US-ASCII. Used for e.g. HTTP.
+ */
+ public String readLine(int maxLength, int bufferSize) throws
IOException {
+ StringBuffer sb = new StringBuffer(bufferSize);
+ while(true) {
+ int x = read();
+ if(x == -1) throw new EOFException();
+ char c = (char) x;
+ if(c == '\n') {
+ if(sb.length() > 0) {
+ if(sb.charAt(sb.length()-1) == '\r')
+ sb.setLength(sb.length()-1);
+ }
+ return sb.toString();
+ }
+ sb.append(c);
+ if(sb.length() >= maxLength)
+ throw new TooLongException();
+ }
+ }
+
+}
Added: trunk/freenet/src/freenet/support/io/TooLongException.java
===================================================================
--- trunk/freenet/src/freenet/support/io/TooLongException.java 2006-01-13
18:09:01 UTC (rev 7852)
+++ trunk/freenet/src/freenet/support/io/TooLongException.java 2006-01-14
01:51:07 UTC (rev 7853)
@@ -0,0 +1,8 @@
+package freenet.support.io;
+
+import java.io.IOException;
+
+/** Exception thrown by a LineReadingInputStream when a line is too long. */
+public class TooLongException extends IOException {
+
+}
\ No newline at end of file