Author: toad
Date: 2006-05-05 17:30:56 +0000 (Fri, 05 May 2006)
New Revision: 8607
Modified:
trunk/freenet/src/freenet/client/DefaultMIMETypes.java
trunk/freenet/src/freenet/client/HighLevelSimpleClient.java
trunk/freenet/src/freenet/client/HighLevelSimpleClientImpl.java
trunk/freenet/src/freenet/clients/http/FproxyToadlet.java
trunk/freenet/src/freenet/clients/http/QueueToadlet.java
trunk/freenet/src/freenet/clients/http/Toadlet.java
trunk/freenet/src/freenet/keys/FreenetURI.java
trunk/freenet/src/freenet/node/Node.java
trunk/freenet/src/freenet/node/Version.java
trunk/freenet/src/freenet/node/fcp/ClientGet.java
trunk/freenet/src/freenet/node/fcp/ClientRequest.java
trunk/freenet/src/freenet/node/fcp/FCPServer.java
trunk/freenet/src/freenet/pluginmanager/HTTPRequest.java
Log:
674: Large-file support in fproxy.
Query the user on a large file download in fproxy.
They can either continue with the download, or queue it to be downloaded to
disk.
Modified: trunk/freenet/src/freenet/client/DefaultMIMETypes.java
===================================================================
--- trunk/freenet/src/freenet/client/DefaultMIMETypes.java 2006-05-05
17:21:30 UTC (rev 8606)
+++ trunk/freenet/src/freenet/client/DefaultMIMETypes.java 2006-05-05
17:30:56 UTC (rev 8607)
@@ -116,6 +116,8 @@
* (y=0; while read x; do echo "$x" |
* sed -n "s/^\([^ ]*\)$/addMIMEType\($y, \"\1\"\);/p;s/^\([^ (),]\+\)
\(.*\)$/addMIMEType\($y, \"\1\", \"\2\"\);/p;"; y=$((y+1)); done)
*/
+
+ // FIXME should we support aliases?
static {
addMIMEType((short)0, "application/activemessage");
@@ -750,4 +752,11 @@
if(typeNumber < 0) return null;
return (String) primaryExtensionByMimeNumber.get(new
Short(typeNumber));
}
+
+ public static boolean isValidExt(String expectedMimeType, String
oldExt) {
+ Short s = (Short) mimeTypesByExtension.get(oldExt);
+ if(s == null) return false;
+ String type = byNumber(s.shortValue());
+ return type.equals(expectedMimeType);
+ }
}
Modified: trunk/freenet/src/freenet/client/HighLevelSimpleClient.java
===================================================================
--- trunk/freenet/src/freenet/client/HighLevelSimpleClient.java 2006-05-05
17:21:30 UTC (rev 8606)
+++ trunk/freenet/src/freenet/client/HighLevelSimpleClient.java 2006-05-05
17:30:56 UTC (rev 8607)
@@ -24,6 +24,11 @@
public FetchResult fetch(FreenetURI uri) throws FetchException;
/**
+ * Blocking fetch of a URI with a configurable max-size.
+ */
+ public FetchResult fetch(FreenetURI uri, long maxSize) throws
FetchException;
+
+ /**
* Blocking insert.
* @throws InserterException If there is an error inserting the data
*/
@@ -47,4 +52,5 @@
* Add a ClientEventListener.
*/
public void addGlobalHook(ClientEventListener listener);
+
}
Modified: trunk/freenet/src/freenet/client/HighLevelSimpleClientImpl.java
===================================================================
--- trunk/freenet/src/freenet/client/HighLevelSimpleClientImpl.java
2006-05-05 17:21:30 UTC (rev 8606)
+++ trunk/freenet/src/freenet/client/HighLevelSimpleClientImpl.java
2006-05-05 17:30:56 UTC (rev 8607)
@@ -99,6 +99,15 @@
return fw.waitForCompletion();
}
+ public FetchResult fetch(FreenetURI uri, long overrideMaxSize) throws
FetchException {
+ if(uri == null) throw new NullPointerException();
+ FetcherContext context = getFetcherContext(overrideMaxSize);
+ FetchWaiter fw = new FetchWaiter();
+ ClientGetter get = new ClientGetter(fw, node.chkFetchScheduler,
node.sskFetchScheduler, uri, context, priorityClass, this, null);
+ get.start();
+ return fw.waitForCompletion();
+ }
+
public FreenetURI insert(InsertBlock insert, boolean getCHKOnly) throws
InserterException {
return insert(insert, getCHKOnly, false);
}
@@ -139,8 +148,18 @@
}
public FetcherContext getFetcherContext() {
+ return getFetcherContext(-1);
+ }
+
+ public FetcherContext getFetcherContext(long overrideMaxSize) {
+ long maxLength = curMaxLength;
+ long maxTempLength = curMaxTempLength;
+ if(overrideMaxSize >= 0) {
+ maxLength = overrideMaxSize;
+ maxTempLength = overrideMaxSize;
+ }
return
- new FetcherContext(curMaxLength, curMaxTempLength,
curMaxMetadataLength,
+ new FetcherContext(maxLength, maxTempLength,
curMaxMetadataLength,
MAX_RECURSION, MAX_ARCHIVE_RESTARTS,
DONT_ENTER_IMPLICIT_ARCHIVES,
SPLITFILE_THREADS, SPLITFILE_BLOCK_RETRIES,
NON_SPLITFILE_RETRIES,
FETCH_SPLITFILES, FOLLOW_REDIRECTS,
LOCAL_REQUESTS_ONLY,
Modified: trunk/freenet/src/freenet/clients/http/FproxyToadlet.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/FproxyToadlet.java 2006-05-05
17:21:30 UTC (rev 8606)
+++ trunk/freenet/src/freenet/clients/http/FproxyToadlet.java 2006-05-05
17:30:56 UTC (rev 8607)
@@ -10,6 +10,7 @@
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
+import freenet.client.DefaultMIMETypes;
import freenet.client.FetchException;
import freenet.client.FetchResult;
import freenet.client.HighLevelSimpleClient;
@@ -23,11 +24,13 @@
import freenet.node.RequestStarter;
import freenet.pluginmanager.HTTPRequest;
import freenet.pluginmanager.PproxyToadlet;
+import freenet.support.Base64;
import freenet.support.Bucket;
import freenet.support.HTMLEncoder;
import freenet.support.HexUtil;
import freenet.support.Logger;
import freenet.support.MultiValueTable;
+import freenet.support.SizeUtil;
import freenet.support.URLEncoder;
public class FproxyToadlet extends Toadlet {
@@ -36,10 +39,14 @@
// ?force= links become invalid after 2 hours.
long FORCE_GRAIN_INTERVAL = 60*60*1000;
+ /** Maximum size for transparent pass-through, should be a config
option */
+ static final long MAX_LENGTH = 2*1024*1024; // 2MB
public FproxyToadlet(HighLevelSimpleClient client, byte[] random) {
super(client);
this.random = random;
+ client.setMaxLength(MAX_LENGTH);
+ client.setMaxIntermediateLength(MAX_LENGTH);
}
public String supportedMethods() {
@@ -112,6 +119,8 @@
if(ks.startsWith("/"))
ks = ks.substring(1);
+ long maxSize = httprequest.getLongParam("max-size", MAX_LENGTH);
+
FreenetURI key;
try {
key = new FreenetURI(ks);
@@ -121,7 +130,7 @@
}
try {
Logger.minor(this, "Fproxy fetching "+key);
- FetchResult result = fetch(key);
+ FetchResult result = fetch(key, maxSize);
// Now, is it safe?
@@ -181,6 +190,67 @@
this.writePermanentRedirect(ctx, "Not enough
meta-strings", "/" + URLEncoder.encode(key.toString(false)) + "/");
} else if(e.newURI != null) {
this.writePermanentRedirect(ctx, msg,
"/"+e.newURI.toString());
+ } else if(e.mode == FetchException.TOO_BIG) {
+ StringBuffer buf = new StringBuffer();
+ ctx.getPageMaker().makeHead(buf, "Large file");
+ buf.append("<table border=\"0\">\n");
+ String fnam = getFilename(e, key,
e.getExpectedMimeType());
+ buf.append("<tr><td><b>Filename</b></td>");
+ buf.append("<a
href=\"/"+URLEncoder.encode(key.toString(false))+"\">");
+ buf.append(fnam);
+ buf.append("</a>");
+ buf.append("</td></tr>\n");
+ boolean finalized = e.finalizedSize();
+ if(e.expectedSize > 0) {
+ buf.append("<tr><td><b>");
+ if(!finalized)
+ buf.append("Expected size (may
change)");
+ else
+ buf.append("Size");
+ buf.append("</b></td><td>");
+
buf.append(SizeUtil.formatSize(e.expectedSize));
+ buf.append("</td></tr>\n");
+ }
+ String mime = e.getExpectedMimeType();
+ if(mime != null) {
+ buf.append("<tr><td><b>");
+ if(!finalized)
+ buf.append("Expected MIME
type");
+ else
+ buf.append("MIME type");
+ buf.append("</b></td><td>");
+ buf.append(mime);
+ buf.append(" bytes </td></tr>\n");
+ }
+ // FIXME filename
+ buf.append("</table>\n");
+ buf.append("<br>This is a large file, so it has
not been streamed direct" +
+ " to your browser, because
Freenet cannot send any data to the " +
+ "browser until it has the whole
file, and this may take some time, " +
+ "and also for resource usage
reasons.\n");
+ buf.append("<br>What would you like to do with
it?:<ul>");
+ buf.append("<li>");
+ buf.append("<form method=\"get\"
action=\"/"+key.toString(false)+"\">");
+ buf.append("<input type=\"hidden\"
name=\"max-size\" value=\""+e.expectedSize+"\">");
+ buf.append("<input type=\"submit\"
name=\"fetch\" value=\"Fetch anyway\">");
+ buf.append("</form></li>");
+ buf.append("<li><form method=\"post\"
action=\"/queue/\">");
+ buf.append("<input type=\"hidden\" name=\"key\"
value=\""+key.toString(false)+"\">");
+ buf.append("<input type=\"hidden\"
name=\"return-type\" value=\"disk\">");
+ buf.append("<input type=\"hidden\"
name=\"persistence\" value=\"forever\">");
+ if(mime != null)
+ buf.append("<input type=\"hidden\"
name=\"type\" value=\""+URLEncoder.encode(mime)+"\">");
+ buf.append("<input type=\"submit\"
name=\"download\" value=\"Download to disk in background\">");
+ buf.append("</form></li>");
+ // FIXME add a queue-a-download option.
+// buf.append("<li>Save it to disk at </li>");
+ // FIXME add return-to-referring-page
+ //buf.append("<li>Return to the referring page:
");
+ buf.append("<li>Return to the fproxy home page:
<a href=\"/\">here</a>");
+ buf.append("</ul>");
+ ctx.getPageMaker().makeTail(buf);
+ writeReply(ctx, 200, "text/html", "OK",
buf.toString());
+ // FIXME provide option to queue write to disk.
} else {
if(e.errorCodes != null)
extra =
"<pre>"+e.errorCodes.toVerboseString()+"</pre>";
@@ -254,4 +324,56 @@
fproxyConfig.finishedInitialization();
}
+
+ /**
+ * Get expected filename for a file.
+ * @param e The FetchException.
+ * @param uri The original URI.
+ * @param expectedMimeType The expected MIME type.
+ */
+ private String getFilename(FetchException e, FreenetURI uri, String
expectedMimeType) {
+ String s = getFilename(e, uri);
+ int dotIdx = s.indexOf('.');
+ String ext = DefaultMIMETypes.getExtension(expectedMimeType);
+ if(ext == null)
+ ext = "bin";
+ if(dotIdx == -1 && expectedMimeType != null) {
+ s += "." + ext;
+ return s;
+ }
+ if(dotIdx != -1) {
+ String oldExt = s.substring(dotIdx+1);
+ if(DefaultMIMETypes.isValidExt(expectedMimeType,
oldExt))
+ return s;
+ return s + "." + ext;
+ }
+ return s + "." + ext;
+ }
+
+ private String getFilename(FetchException e, FreenetURI uri) {
+ String fnam = sanitize(uri.getDocName());
+ if(fnam != null && fnam.length() > 0) return fnam;
+ String[] meta = uri.getAllMetaStrings();
+ for(int i=meta.length-1;i>=0;i++) {
+ String s = meta[i];
+ if(s.length() == 0) continue;
+ fnam = sanitize(s);
+ if(s != null && s.length() > 0) return fnam;
+ }
+ return Base64.encode(uri.getRoutingKey());
+ }
+
+ private String sanitize(String s) {
+ if(s == null) return null;
+ StringBuffer sb = new StringBuffer(s.length());
+ for(int i=0;i<s.length();i++) {
+ char c = s.charAt(i);
+ if(Character.isLetterOrDigit(c) ||
+ c == ' ' || c == '.' || c == '-' || c
== '_' ||
+ c == '+' || c == ',')
+ sb.append(c);
+ }
+ return sb.toString();
+ }
+
}
Modified: trunk/freenet/src/freenet/clients/http/QueueToadlet.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/QueueToadlet.java 2006-05-05
17:21:30 UTC (rev 8606)
+++ trunk/freenet/src/freenet/clients/http/QueueToadlet.java 2006-05-05
17:30:56 UTC (rev 8607)
@@ -2,8 +2,8 @@
import java.io.File;
import java.io.IOException;
+import java.net.MalformedURLException;
import java.net.URI;
-import java.net.URISyntaxException;
import java.text.NumberFormat;
import java.util.Iterator;
import java.util.LinkedList;
@@ -12,15 +12,12 @@
import freenet.keys.FreenetURI;
import freenet.node.fcp.ClientGet;
import freenet.node.fcp.ClientPut;
-import freenet.node.fcp.ClientPutBase;
import freenet.node.fcp.ClientPutDir;
import freenet.node.fcp.ClientRequest;
import freenet.node.fcp.FCPServer;
import freenet.node.fcp.MessageInvalidException;
import freenet.pluginmanager.HTTPRequest;
import freenet.support.Bucket;
-import freenet.support.BucketTools;
-import freenet.support.Fields;
import freenet.support.HTMLEncoder;
import freenet.support.Logger;
import freenet.support.SizeUtil;
@@ -41,27 +38,46 @@
this.writeReply(ctx, 400, "text/plain", "Too big",
"Data exceeds 1MB limit");
return;
}
- byte[] d = BucketTools.toByteArray(data);
- String s = new String(d, "us-ascii");
- HTTPRequest request;
- try {
- request = new HTTPRequest("/", s);
- } catch (URISyntaxException e) {
- Logger.error(this, "Impossible: "+e, e);
- return;
- }
+ HTTPRequest request = new HTTPRequest(uri, data, ctx);
if(request.isParameterSet("remove_request") &&
request.getParam("remove_request").length() > 0) {
String identifier = request.getParam("identifier");
+ Logger.minor(this, "Removing "+identifier);
try {
fcp.removeGlobalRequest(identifier);
} catch (MessageInvalidException e) {
- this.sendErrorPage(ctx, 200, "OK", "Failed to
remove "+HTMLEncoder.encode(identifier)+" :
"+HTMLEncoder.encode(e.getMessage()));
+ this.sendErrorPage(ctx, 200, "Failed to remove
request", "Failed to remove "+HTMLEncoder.encode(identifier)+" :
"+HTMLEncoder.encode(e.getMessage()));
}
}
+ if(request.isParameterSet("download")) {
+ // Queue a download
+ if(!request.isParameterSet("key")) {
+ writeError("No key specified to download", "No
key specified to download");
+ return;
+ }
+ String expectedMIMEType = null;
+ if(request.isParameterSet("type")) {
+ expectedMIMEType = request.getParam("type");
+ }
+ FreenetURI fetchURI;
+ try {
+ fetchURI = new
FreenetURI(request.getParam("key"));
+ } catch (MalformedURLException e) {
+ writeError("Invalid URI to download", "Invalid
URI to download");
+ return;
+ }
+ String persistence = request.getParam("persistence");
+ String returnType = request.getParam("return-type");
+ fcp.makePersistentGlobalRequest(fetchURI,
expectedMIMEType, persistence, returnType);
+ }
this.handleGet(uri, ctx);
}
+ private void writeError(String string, String string2) {
+ // TODO Auto-generated method stub
+
+ }
+
public void handleGet(URI uri, ToadletContext ctx)
throws ToadletContextClosedException, IOException, RedirectException {
@@ -396,7 +412,7 @@
buf.append("<td>");
buf.append("<form action=\"/queue/\" method=\"post\">");
buf.append("<input type=\"hidden\" name=\"identifier\"
value=\"");
- buf.append(URLEncoder.encode(p.getIdentifier()));
+ buf.append(HTMLEncoder.encode(p.getIdentifier()));
buf.append("\"><input type=\"submit\" name=\"remove_request\"
value=\"Delete\">");
buf.append("</form>\n");
buf.append("</td>\n");
Modified: trunk/freenet/src/freenet/clients/http/Toadlet.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/Toadlet.java 2006-05-05 17:21:30 UTC
(rev 8606)
+++ trunk/freenet/src/freenet/clients/http/Toadlet.java 2006-05-05 17:30:56 UTC
(rev 8607)
@@ -107,10 +107,11 @@
/**
* Client calls from the above messages to run a freenet request.
* This method may block (or suspend).
+ * @param maxSize Maximum length of returned content.
*/
- FetchResult fetch(FreenetURI uri) throws FetchException {
+ FetchResult fetch(FreenetURI uri, long maxSize) throws FetchException {
// For now, just run it blocking.
- return client.fetch(uri);
+ return client.fetch(uri, maxSize);
}
FreenetURI insert(InsertBlock insert, boolean getCHKOnly) throws
InserterException {
Modified: trunk/freenet/src/freenet/keys/FreenetURI.java
===================================================================
--- trunk/freenet/src/freenet/keys/FreenetURI.java 2006-05-05 17:21:30 UTC
(rev 8606)
+++ trunk/freenet/src/freenet/keys/FreenetURI.java 2006-05-05 17:30:56 UTC
(rev 8607)
@@ -602,4 +602,57 @@
else throw new IllegalArgumentException("Not a USK requesting
suggested edition");
}
+ public String getPreferredFilename() {
+ Logger.minor(this, "Getting preferred filename for "+this);
+ Vector names = new Vector();
+ if(keyType.equals("KSK") || keyType.equals("SSK")) {
+ Logger.minor(this, "Adding docName: "+docName);
+ names.add(docName);
+ }
+ if(metaStr != null)
+ for(int i=0;i<metaStr.length;i++) {
+ Logger.minor(this, "Adding metaString "+i+":
"+metaStr[i]);
+ names.add(metaStr[i]);
+ }
+ StringBuffer out = new StringBuffer();
+ for(int i=0;i<names.size();i++) {
+ String s = (String) names.get(i);
+ Logger.minor(this, "name "+i+" = "+s);
+ s = sanitize(s);
+ Logger.minor(this, "Sanitized name "+i+" = "+s);
+ if(s.length() > 0) {
+ if(out.length() > 0)
+ out.append("-");
+ out.append(s);
+ }
+ }
+ Logger.minor(this, "out = "+out.toString());
+ if(out.length() == 0) {
+ if(routingKey != null) {
+ Logger.minor(this, "Returning base64 encoded
routing key");
+ return Base64.encode(routingKey);
+ }
+ return "unknown";
+ }
+ return out.toString();
+ }
+
+ private String sanitize(String s) {
+ StringBuffer sb = new StringBuffer(s.length());
+ for(int i=0;i<s.length();i++) {
+ char c = s.charAt(i);
+ if(c == '/' || c == '\\' || c == '%' || c == '>' || c
== '<' || c == ':' || c == '\'' || c == '\"')
+ continue;
+ if(Character.isDigit(c))
+ sb.append(c);
+ else if(Character.isLetter(c))
+ sb.append(c);
+ else if(Character.isWhitespace(c))
+ sb.append(' ');
+ else if(c == '-' || c == '_' || c == '.')
+ sb.append(c);
+ }
+ return sb.toString();
+ }
+
}
Modified: trunk/freenet/src/freenet/node/Node.java
===================================================================
--- trunk/freenet/src/freenet/node/Node.java 2006-05-05 17:21:30 UTC (rev
8606)
+++ trunk/freenet/src/freenet/node/Node.java 2006-05-05 17:30:56 UTC (rev
8607)
@@ -2292,4 +2292,8 @@
public void removeDarknetConnection(PeerNode pn) {
peers.disconnect(pn);
}
+
+ public File getDownloadDir() {
+ return downloadDir;
+ }
}
Modified: trunk/freenet/src/freenet/node/Version.java
===================================================================
--- trunk/freenet/src/freenet/node/Version.java 2006-05-05 17:21:30 UTC (rev
8606)
+++ trunk/freenet/src/freenet/node/Version.java 2006-05-05 17:30:56 UTC (rev
8607)
@@ -20,7 +20,7 @@
public static final String protocolVersion = "1.0";
/** The build number of the current revision */
- private static final int buildNumber = 673;
+ private static final int buildNumber = 674;
/** Oldest build of Fred we will talk to */
private static final int lastGoodBuild = 591;
Modified: trunk/freenet/src/freenet/node/fcp/ClientGet.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/ClientGet.java 2006-05-05 17:21:30 UTC
(rev 8606)
+++ trunk/freenet/src/freenet/node/fcp/ClientGet.java 2006-05-05 17:30:56 UTC
(rev 8607)
@@ -61,6 +61,59 @@
/** Last progress message. Not persistent - FIXME this will be made
persistent
* when we have proper persistence at the ClientGetter level. */
private SimpleProgressMessage progressPending;
+
+ /**
+ * Create one for a global-queued request not made by FCP.
+ * @throws IdentifierCollisionException
+ */
+ public ClientGet(FCPClient globalClient, FreenetURI uri, boolean
dsOnly, boolean ignoreDS,
+ int maxSplitfileRetries, int maxNonSplitfileRetries,
long maxOutputLength,
+ short returnType, boolean persistRebootOnly, String
identifier, int verbosity, short prioClass,
+ File returnFilename, File returnTempFilename) throws
IdentifierCollisionException {
+ super(uri, identifier, verbosity, null, globalClient,
prioClass,
+ (persistRebootOnly ?
ClientRequest.PERSIST_REBOOT : ClientRequest.PERSIST_FOREVER),
+ null, true);
+ fctx = new FetcherContext(client.defaultFetchContext,
FetcherContext.IDENTICAL_MASK, false);
+ fctx.eventProducer.addEventListener(this);
+ fctx.localRequestOnly = dsOnly;
+ fctx.ignoreStore = ignoreDS;
+ fctx.maxNonSplitfileRetries = maxNonSplitfileRetries;
+ fctx.maxSplitfileBlockRetries = maxSplitfileRetries;
+ fctx.maxOutputLength = maxOutputLength;
+ fctx.maxTempLength = maxOutputLength;
+ Bucket ret = null;
+ this.returnType = returnType;
+ if(returnType == ClientGetMessage.RETURN_TYPE_DISK) {
+ this.targetFile = returnFilename;
+ this.tempFile = returnTempFilename;
+ ret = new FileBucket(returnTempFilename, false, false,
false, false);
+ } else if(returnType == ClientGetMessage.RETURN_TYPE_NONE) {
+ targetFile = null;
+ tempFile = null;
+ ret = new NullBucket();
+ } else {
+ targetFile = null;
+ tempFile = null;
+ try {
+ ret =
client.server.node.persistentTempBucketFactory.makeEncryptedBucket();
+ } catch (IOException e) {
+ onFailure(new
FetchException(FetchException.BUCKET_ERROR), null);
+ getter = null;
+ returnBucket = null;
+ return;
+ }
+ }
+ returnBucket = ret;
+ if(persistenceType != PERSIST_CONNECTION)
+ try {
+ client.register(this);
+ } catch (IdentifierCollisionException e) {
+ ret.free();
+ throw e;
+ }
+ getter = new ClientGetter(this, client.node.chkFetchScheduler,
client.node.sskFetchScheduler, uri, fctx, priorityClass, client, returnBucket);
+ }
+
public ClientGet(FCPConnectionHandler handler, ClientGetMessage
message) throws IdentifierCollisionException {
super(message.uri, message.identifier, message.verbosity,
handler, message.priorityClass,
@@ -102,7 +155,12 @@
}
returnBucket = ret;
if(persistenceType != PERSIST_CONNECTION)
- client.register(this);
+ try {
+ client.register(this);
+ } catch (IdentifierCollisionException e) {
+ ret.free();
+ throw e;
+ }
getter = new ClientGetter(this, client.node.chkFetchScheduler,
client.node.sskFetchScheduler, uri, fctx, priorityClass, client, returnBucket);
if(persistenceType != PERSIST_CONNECTION && handler != null)
sendPendingMessages(handler.outputHandler, true, true);
Modified: trunk/freenet/src/freenet/node/fcp/ClientRequest.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/ClientRequest.java 2006-05-05
17:21:30 UTC (rev 8606)
+++ trunk/freenet/src/freenet/node/fcp/ClientRequest.java 2006-05-05
17:30:56 UTC (rev 8607)
@@ -39,6 +39,23 @@
protected String clientToken;
/** Is the request on the global queue? */
protected final boolean global;
+
+ public ClientRequest(FreenetURI uri2, String identifier2, int
verbosity2, FCPConnectionHandler handler,
+ FCPClient client, short priorityClass2, short
persistenceType2, String clientToken2, boolean global) {
+ this.uri = uri2;
+ this.identifier = identifier2;
+ this.verbosity = verbosity2;
+ this.finished = false;
+ this.priorityClass = priorityClass2;
+ this.persistenceType = persistenceType2;
+ this.clientToken = clientToken2;
+ this.global = global;
+ if(persistenceType == PERSIST_CONNECTION)
+ this.origHandler = handler;
+ else
+ origHandler = null;
+ this.client = client;
+ }
public ClientRequest(FreenetURI uri2, String identifier2, int
verbosity2, FCPConnectionHandler handler,
short priorityClass2, short persistenceType2, String
clientToken2, boolean global) {
Modified: trunk/freenet/src/freenet/node/fcp/FCPServer.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/FCPServer.java 2006-05-05 17:21:30 UTC
(rev 8606)
+++ trunk/freenet/src/freenet/node/fcp/FCPServer.java 2006-05-05 17:30:56 UTC
(rev 8607)
@@ -18,6 +18,8 @@
import java.util.Vector;
import java.util.WeakHashMap;
+import freenet.client.ClientMetadata;
+import freenet.client.DefaultMIMETypes;
import freenet.client.FetcherContext;
import freenet.client.HighLevelSimpleClient;
import freenet.client.InserterContext;
@@ -29,7 +31,10 @@
import freenet.config.StringCallback;
import freenet.config.SubConfig;
import freenet.io.NetworkInterface;
+import freenet.keys.FreenetURI;
import freenet.node.Node;
+import freenet.node.RequestStarter;
+import freenet.support.Base64;
import freenet.support.Logger;
/**
@@ -57,6 +62,8 @@
private long persistenceInterval;
final FetcherContext defaultFetchContext;
public InserterContext defaultInsertContext;
+ public static final int QUEUE_MAX_RETRIES = -1;
+ public static final long QUEUE_MAX_DATA_SIZE = Long.MAX_VALUE;
private void startPersister() {
Thread t = new Thread(persister = new FCPServerPersister(),
"FCP request persistence handler");
@@ -543,4 +550,92 @@
globalClient.removeByIdentifier(identifier, true);
}
+ /**
+ * Create a persistent globally-queued request for a file.
+ * @param fetchURI The file to fetch.
+ * @param persistence The persistence type.
+ * @param returnType The return type.
+ */
+ public void makePersistentGlobalRequest(FreenetURI fetchURI, String
expectedMimeType, String persistenceTypeString, String returnTypeString) {
+ boolean persistence =
persistenceTypeString.equalsIgnoreCase("reboot");
+ short returnType =
ClientGetMessage.parseReturnType(returnTypeString);
+ File returnFilename = null, returnTempFilename = null;
+ if(returnType == ClientGetMessage.RETURN_TYPE_DISK) {
+ returnFilename = makeReturnFilename(fetchURI,
expectedMimeType);
+ returnTempFilename =
makeTempReturnFilename(returnFilename);
+ }
+// public ClientGet(FCPClient globalClient, FreenetURI uri,
boolean dsOnly, boolean ignoreDS,
+// int maxSplitfileRetries, int
maxNonSplitfileRetries, long maxOutputLength,
+// short returnType, boolean persistRebootOnly,
String identifier, int verbosity, short prioClass,
+// File returnFilename, File returnTempFilename)
throws IdentifierCollisionException {
+
+ try {
+ innerMakePersistentGlobalRequest(fetchURI, persistence,
returnType, "FProxy:"+fetchURI.getPreferredFilename(), returnFilename,
returnTempFilename);
+ return;
+ } catch (IdentifierCollisionException ee) {
+ try {
+ innerMakePersistentGlobalRequest(fetchURI,
persistence, returnType, "FProxy:"+fetchURI.getDocName(), returnFilename,
returnTempFilename);
+ return;
+ } catch (IdentifierCollisionException e) {
+ try {
+
innerMakePersistentGlobalRequest(fetchURI, persistence, returnType,
"FProxy:"+fetchURI.toString(false), returnFilename, returnTempFilename);
+ return;
+ } catch (IdentifierCollisionException e1) {
+ // FIXME maybe use DateFormat
+ try {
+
innerMakePersistentGlobalRequest(fetchURI, persistence, returnType, "FProxy
("+System.currentTimeMillis()+")", returnFilename, returnTempFilename);
+ return;
+ } catch (IdentifierCollisionException
e2) {
+ while(true) {
+ byte[] buf = new
byte[8];
+ try {
+
node.random.nextBytes(buf);
+ String id =
"FProxy:"+Base64.encode(buf);
+
innerMakePersistentGlobalRequest(fetchURI, persistence, returnType, id,
returnFilename, returnTempFilename);
+ return;
+ } catch
(IdentifierCollisionException e3) {};
+ }
+ }
+ }
+ }
+
+ }
+ }
+
+ private File makeTempReturnFilename(File returnFilename) {
+ return new File(returnFilename.toString() + ".freenet-tmp");
+ }
+
+ private File makeReturnFilename(FreenetURI uri, String
expectedMimeType) {
+ String ext;
+ if(expectedMimeType != null && expectedMimeType.length() > 0 &&
+
!expectedMimeType.equals(DefaultMIMETypes.DEFAULT_MIME_TYPE)) {
+ ext = DefaultMIMETypes.getExtension(expectedMimeType);
+ } else ext = null;
+ String extAdd = (ext == null ? "" : "." + ext);
+ String preferred = uri.getPreferredFilename();
+ File f = new File(node.getDownloadDir(), preferred + extAdd);
+ File f1 = new File(node.getDownloadDir(), preferred +
".freenet-tmp");
+ int x = 0;
+ while(f.exists() || f1.exists()) {
+ f = new File(node.getDownloadDir(), preferred + "-" + x
+ extAdd);
+ f1 = new File(node.getDownloadDir(), preferred + "-" +
x + extAdd + ".freenet-tmp");
+ }
+ return f;
+ }
+
+ private void innerMakePersistentGlobalRequest(FreenetURI fetchURI,
boolean persistRebootOnly, short returnType, String id, File returnFilename,
+ File returnTempFilename) throws
IdentifierCollisionException {
+ ClientGet cg =
+ new ClientGet(globalClient, fetchURI,
defaultFetchContext.localRequestOnly,
+ defaultFetchContext.ignoreStore,
QUEUE_MAX_RETRIES, QUEUE_MAX_RETRIES,
+ QUEUE_MAX_DATA_SIZE, returnType,
persistRebootOnly, id, Integer.MAX_VALUE,
+
RequestStarter.BULK_SPLITFILE_PRIORITY_CLASS, returnFilename,
returnTempFilename);
+ // Register before starting, because it may complete
immediately, and if it does,
+ // we may end up with it not being removable because it wasn't
registered!
+ if(cg.isPersistentForever())
+ forceStorePersistentRequests();
+ cg.start();
+ }
+
}
Modified: trunk/freenet/src/freenet/pluginmanager/HTTPRequest.java
===================================================================
--- trunk/freenet/src/freenet/pluginmanager/HTTPRequest.java 2006-05-05
17:21:30 UTC (rev 8606)
+++ trunk/freenet/src/freenet/pluginmanager/HTTPRequest.java 2006-05-05
17:30:56 UTC (rev 8607)
@@ -17,6 +17,7 @@
import freenet.clients.http.ToadletContext;
import freenet.support.Bucket;
import freenet.support.BucketFactory;
+import freenet.support.BucketTools;
import freenet.support.Logger;
import freenet.support.MultiValueTable;
import freenet.support.URLDecoder;
@@ -98,7 +99,9 @@
}
/**
- * Creates a new HTTPRequest for the given URI and data (for
multipart/form-data)
+ * Creates a new HTTPRequest for the given URI and data.
+ * multipart/form-data will be split into Part's, but
+ * application/x-www-form-urlencoded will be split into Param's.
*
* @param uri The URI being requested
* @param h Client headers
@@ -386,10 +389,23 @@
// TODO: add similar methods for multiple long, boolean etc.
+ /**
+ * Parse submitted data from a bucket.
+ * Note that if this is application/x-www-form-urlencoded, it will come
out as
+ * params, whereas if it is multipart/form-data it will be separated
into buckets.
+ */
private void parseMultiPartData() throws IOException {
String ctype = (String) this.headers.get("content-type");
if (ctype == null) return;
String[] ctypeparts = ctype.split(";");
+
if(ctypeparts[0].equalsIgnoreCase("application/x-www-form-urlencoded")) {
+ // Completely different encoding, but easy to handle
+ if(data.size() > 1024*1024)
+ throw new IOException("Too big");
+ byte[] buf = BucketTools.toByteArray(data);
+ String s = new String(buf, "us-ascii");
+ parseRequestParameters(s, true);
+ }
if
(!ctypeparts[0].trim().equalsIgnoreCase("multipart/form-data") ||
ctypeparts.length < 2) {
return;
}
@@ -527,4 +543,16 @@
b.free();
}
}
+
+ public long getLongParam(String name, long defaultValue) {
+ if (!this.isParameterSet(name)) {
+ return defaultValue;
+ }
+ String value = this.getParameterValue(name);
+ try {
+ return Long.parseLong(value);
+ } catch (NumberFormatException e) {
+ return defaultValue;
+ }
+ }
}